/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.apache.catalina.core;

import org.apache.catalina.*;
import org.apache.catalina.deploy.*;
import org.apache.catalina.loader.WebappLoader;
import org.apache.catalina.session.StandardManager;
import org.apache.catalina.startup.TldConfig;
import org.apache.catalina.util.*;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.naming.ContextBindings;
import org.apache.naming.resources.*;
import org.apache.tomcat.InstanceManager;
import org.apache.tomcat.JarScanner;
import org.apache.tomcat.util.ExceptionUtils;
import org.apache.tomcat.util.descriptor.XmlIdentifiers;
import org.apache.tomcat.util.modeler.Registry;
import org.apache.tomcat.util.scan.StandardJarScanner;

import javax.management.*;
import javax.naming.NamingException;
import javax.naming.directory.DirContext;
import javax.servlet.*;
import javax.servlet.ServletRegistration.Dynamic;
import javax.servlet.descriptor.JspConfigDescriptor;
import javax.servlet.http.HttpSessionAttributeListener;
import javax.servlet.http.HttpSessionListener;
import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.*;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicLong;

/**
 * Standard implementation of the <b>Context</b> interface.  Each
 * child container must be a Wrapper implementation to process the
 * requests directed to a particular servlet.
 *
 * @author Craig R. McClanahan
 * @author Remy Maucherat
 */
public class StandardContext extends ContainerBase
		implements Context, NotificationEmitter {

	private static final Log log = LogFactory.getLog(StandardContext.class);


	// ----------------------------------------------------------- Constructors
	/**
	 * The descriptive information string for this implementation.
	 */
	private static final String info =
			"org.apache.catalina.core.StandardContext/1.0";


	// ----------------------------------------------------- Class Variables
	/**
	 * Array containing the safe characters set.
	 */
	protected static URLEncoder urlEncoder;

	/**
	 * GMT timezone - all HTTP dates are on GMT
	 */
	static {
		urlEncoder = new URLEncoder();
		urlEncoder.addSafeCharacter('~');
		urlEncoder.addSafeCharacter('-');
		urlEncoder.addSafeCharacter('_');
		urlEncoder.addSafeCharacter('.');
		urlEncoder.addSafeCharacter('*');
		urlEncoder.addSafeCharacter('/');
	}

	private final Object applicationListenersLock = new Object();


	// ----------------------------------------------------- Instance Variables
	/**
	 * The set of application listeners that are required to have limited access
	 * to ServletContext methods. See Servlet 3.0 section 4.4.
	 */

	private final Set<Object> noPluggabilityListeners = new HashSet<Object>();
	private final Object applicationParametersLock = new Object();
	private final Object constraintsLock = new Object();
	/**
	 * The set of filter mappings for this application, in the order
	 * they were defined in the deployment descriptor with additional mappings
	 * added via the {@link ServletContext} possibly both before and after those
	 * defined in the deployment descriptor.
	 */
	private final ContextFilterMaps filterMaps = new ContextFilterMaps();
	private final Object instanceListenersLock = new Object();
	/**
	 * The context initialization parameters for this web application,
	 * keyed by name.
	 */
	private final ConcurrentMap<String, String> parameters = new ConcurrentHashMap<String, String>();
	private final Object securityRolesLock = new Object();
	private final Object servletMappingsLock = new Object();
	private final Object watchedResourcesLock = new Object();
	private final Object welcomeFilesLock = new Object();
	private final Object wrapperLifecyclesLock = new Object();
	private final Object wrapperListenersLock = new Object();
	/**
	 * Allow multipart/form-data requests to be parsed even when the
	 * target servlet doesn't specify @MultipartConfig or have a
	 * &lt;multipart-config&gt; element.
	 */
	protected boolean allowCasualMultipartParsing = false;
	/**
	 * The ServletContext implementation associated with this Context.
	 */
	protected ApplicationContext context = null;
	/**
	 * Allow linking.
	 */
	protected boolean allowLinking = false;
	/**
	 * Cache max size in KB.
	 */
	protected int cacheMaxSize = 10240; // 10 MB
	/**
	 * Cache object max size in KB.
	 */
	protected int cacheObjectMaxSize = 512; // 512K
	/**
	 * Cache TTL in ms.
	 */
	protected int cacheTTL = 5000;
	/**
	 * Control whether remaining request data will be read
	 * (swallowed) even if the request violates a data size constraint.
	 */
	private boolean swallowAbortedUploads = true;
	/**
	 * The alternate deployment descriptor name.
	 */
	private String altDDName = null;
	/**
	 * Lifecycle provider.
	 */
	private InstanceManager instanceManager = null;
	/**
	 * Associated host name.
	 */
	private String hostName;
	/**
	 * The antiJARLocking flag for this Context.
	 */
	private boolean antiJARLocking = false;
	/**
	 * The antiResourceLocking flag for this Context.
	 */
	private boolean antiResourceLocking = false;
	/**
	 * The set of application listener class names configured for this
	 * application, in the order they were encountered in the resulting merged
	 * web.xml file.
	 */
	private ApplicationListener applicationListeners[] =
			new ApplicationListener[0];
	/**
	 * The set of instantiated application event listener objects. Note that
	 * SCIs and other code may use the pluggability APIs to add listener
	 * instances directly to this list before the application starts.
	 */
	private Object applicationEventListenersObjects[] =
			new Object[0];
	/**
	 * The set of instantiated application lifecycle listener objects. Note that
	 * SCIs and other code may use the pluggability APIs to add listener
	 * instances directly to this list before the application starts.
	 */
	private Object applicationLifecycleListenersObjects[] =
			new Object[0];
	/**
	 * The ordered set of ServletContainerInitializers for this web application.
	 */
	private Map<ServletContainerInitializer, Set<Class<?>>> initializers =
			new LinkedHashMap<ServletContainerInitializer, Set<Class<?>>>();
	/**
	 * The set of application parameters defined for this application.
	 */
	private ApplicationParameter applicationParameters[] =
			new ApplicationParameter[0];
	/**
	 * The broadcaster that sends j2ee notifications.
	 */
	private NotificationBroadcasterSupport broadcaster = null;
	/**
	 * The Locale to character set mapper for this application.
	 */
	private CharsetMapper charsetMapper = null;
	/**
	 * The Java class name of the CharsetMapper class to be created.
	 */
	private String charsetMapperClass =
			"org.apache.catalina.util.CharsetMapper";
	/**
	 * The URL of the XML descriptor for this context.
	 */
	private URL configFile = null;
	/**
	 * The "correctly configured" flag for this Context.
	 */
	private boolean configured = false;
	/**
	 * The security constraints for this web application.
	 */
	private volatile SecurityConstraint constraints[] =
			new SecurityConstraint[0];
	/**
	 * The wrapped version of the associated ServletContext that is presented
	 * to listeners that are required to have limited access to ServletContext
	 * methods. See Servlet 3.0 section 4.4.
	 */
	private NoPluggabilityServletContext noPluggabilityServletContext = null;
	/**
	 * Compiler classpath to use.
	 */
	private String compilerClasspath = null;
	/**
	 * Should we attempt to use cookies for session id communication?
	 */
	private boolean cookies = true;
	/**
	 * Should we allow the <code>ServletContext.getContext()</code> method
	 * to access the context of other web applications in this server?
	 */
	private boolean crossContext = false;
	/**
	 * Encoded path.
	 */
	private String encodedPath = null;
	/**
	 * Unencoded path for this web application.
	 */
	private String path = null;
	/**
	 * The "follow standard delegation model" flag that will be used to
	 * configure our ClassLoader.
	 */
	private boolean delegate = false;
	/**
	 * The display name of this web application.
	 */
	private String displayName = null;
	/**
	 * Override the default context xml location.
	 */
	private String defaultContextXml;
	/**
	 * Override the default web xml location.
	 */
	private String defaultWebXml;
	/**
	 * The distributable flag for this web application.
	 */
	private boolean distributable = false;
	/**
	 * The document root for this web application.
	 */
	private String docBase = null;
	/**
	 * The exception pages for this web application, keyed by fully qualified
	 * class name of the Java exception.
	 */
	private HashMap<String, ErrorPage> exceptionPages =
			new HashMap<String, ErrorPage>();
	/**
	 * The set of filter configurations (and associated filter instances) we
	 * have initialized, keyed by filter name.
	 */
	private HashMap<String, ApplicationFilterConfig> filterConfigs =
			new HashMap<String, ApplicationFilterConfig>();
	/**
	 * The set of filter definitions for this application, keyed by
	 * filter name.
	 */
	private HashMap<String, FilterDef> filterDefs =
			new HashMap<String, FilterDef>();
	/**
	 * Ignore annotations.
	 */
	private boolean ignoreAnnotations = false;
	/**
	 * The set of classnames of InstanceListeners that will be added
	 * to each newly created Wrapper by <code>createWrapper()</code>.
	 */
	private String instanceListeners[] = new String[0];
	/**
	 * The login configuration descriptor for this web application.
	 */
	private LoginConfig loginConfig = null;
	/**
	 * The mapper associated with this context.
	 */
	private org.apache.tomcat.util.http.mapper.Mapper mapper =
			new org.apache.tomcat.util.http.mapper.Mapper();
	/**
	 * The naming context listener for this web application.
	 */
	private NamingContextListener namingContextListener = null;
	/**
	 * The naming resources for this web application.
	 */
	private NamingResources namingResources = null;
	/**
	 * The message destinations for this web application.
	 */
	private HashMap<String, MessageDestination> messageDestinations =
			new HashMap<String, MessageDestination>();
	/**
	 * The MIME mappings for this web application, keyed by extension.
	 */
	private HashMap<String, String> mimeMappings =
			new HashMap<String, String>();
	/**
	 * Special case: error page for status 200.
	 */
	private ErrorPage okErrorPage = null;
	/**
	 * The request processing pause flag (while reloading occurs)
	 */
	private volatile boolean paused = false;
	/**
	 * The public identifier of the DTD for the web application deployment
	 * descriptor version we are currently parsing.  This is used to support
	 * relaxed validation rules when processing version 2.2 web.xml files.
	 */
	private String publicId = null;
	/**
	 * The reloadable flag for this web application.
	 */
	private boolean reloadable = false;
	/**
	 * Unpack WAR property.
	 */
	private boolean unpackWAR = true;
	/**
	 * Context level override for default {@link StandardHost#isCopyXML()}.
	 */
	private boolean copyXML = false;
	/**
	 * The default context override flag for this web application.
	 */
	private boolean override = false;
	/**
	 * The original document root for this web application.
	 */
	private String originalDocBase = null;
	/**
	 * The privileged flag for this web application.
	 */
	private boolean privileged = false;
	/**
	 * Should the next call to <code>addWelcomeFile()</code> cause replacement
	 * of any existing welcome files?  This will be set before processing the
	 * web application's deployment descriptor, so that application specified
	 * choices <strong>replace</strong>, rather than append to, those defined
	 * in the global descriptor.
	 */
	private boolean replaceWelcomeFiles = false;
	/**
	 * The security role mappings for this application, keyed by role
	 * name (as used within the application).
	 */
	private HashMap<String, String> roleMappings =
			new HashMap<String, String>();
	/**
	 * The security roles for this application, keyed by role name.
	 */
	private String securityRoles[] = new String[0];
	/**
	 * The servlet mappings for this web application, keyed by
	 * matching pattern.
	 */
	private HashMap<String, String> servletMappings =
			new HashMap<String, String>();
	/**
	 * The session timeout (in minutes) for this web application.
	 */
	private int sessionTimeout = 30;
	/**
	 * The notification sequence number.
	 */
	private AtomicLong sequenceNumber = new AtomicLong(0);
	/**
	 * The status code error pages for this web application, keyed by
	 * HTTP status code (as an Integer). Note status code zero is used for the
	 * default error page.
	 */
	private HashMap<Integer, ErrorPage> statusPages =
			new HashMap<Integer, ErrorPage>();
	/**
	 * Set flag to true to cause the system.out and system.err to be redirected
	 * to the logger when executing a servlet.
	 */
	private boolean swallowOutput = false;
	/**
	 * Amount of ms that the container will wait for servlets to unload.
	 */
	private long unloadDelay = 2000;
	/**
	 * The watched resources for this application.
	 */
	private String watchedResources[] = new String[0];
	/**
	 * The welcome files for this application.
	 */
	private String welcomeFiles[] = new String[0];
	/**
	 * The set of classnames of LifecycleListeners that will be added
	 * to each newly created Wrapper by <code>createWrapper()</code>.
	 */
	private String wrapperLifecycles[] = new String[0];
	/**
	 * The set of classnames of ContainerListeners that will be added
	 * to each newly created Wrapper by <code>createWrapper()</code>.
	 */
	private String wrapperListeners[] = new String[0];
	/**
	 * The pathname to the work directory for this context (relative to
	 * the server's home if not absolute).
	 */
	private String workDir = null;
	/**
	 * Java class name of the Wrapper class implementation we use.
	 */
	private String wrapperClassName = StandardWrapper.class.getName();
	private Class<?> wrapperClass = null;
	/**
	 * JNDI use flag.
	 */
	private boolean useNaming = true;
	/**
	 * Filesystem based flag.
	 */
	private boolean filesystemBased = false;
	/**
	 * Name of the associated naming context.
	 */
	private String namingContextName = null;
	/**
	 * Caching allowed flag.
	 */
	private boolean cachingAllowed = true;
	/**
	 * Attribute used to turn on/off the use of external entities.
	 */
	private boolean xmlBlockExternal = true;
	/**
	 * List of resource aliases.
	 */
	private String aliases = null;
	/**
	 * Non proxied resources.
	 */
	private DirContext webappResources = null;
	private long startupTime;
	private long startTime;
	private long tldScanTime;
	/**
	 * Name of the engine. If null, the domain is used.
	 */
	private String j2EEApplication = "none";
	private String j2EEServer = "none";
	/**
	 * Attribute value used to turn on/off XML validation for web.xml and
	 * web-fragment.xml files.
	 */
	private boolean webXmlValidation = Globals.STRICT_SERVLET_COMPLIANCE;
	/**
	 * Attribute value used to turn on/off XML namespace validation
	 */
	private boolean webXmlNamespaceAware = Globals.STRICT_SERVLET_COMPLIANCE;
	/**
	 * Attribute value used to turn on/off TLD processing
	 */
	private boolean processTlds = true;
	/**
	 * Attribute value used to turn on/off XML validation
	 */
	private boolean tldValidation = Globals.STRICT_SERVLET_COMPLIANCE;
	/**
	 * Should we save the configuration.
	 */
	private boolean saveConfig = true;
	/**
	 * The name to use for session cookies. <code>null</code> indicates that
	 * the name is controlled by the application.
	 */
	private String sessionCookieName;
	/**
	 * The flag that indicates that session cookies should use HttpOnly
	 */
	private boolean useHttpOnly = true;
	/**
	 * The domain to use for session cookies. <code>null</code> indicates that
	 * the domain is controlled by the application.
	 */
	private String sessionCookieDomain;
	/**
	 * The path to use for session cookies. <code>null</code> indicates that
	 * the path is controlled by the application.
	 */
	private String sessionCookiePath;
	/**
	 * Is a / added to the end of the session cookie path to ensure browsers,
	 * particularly IE, don't send a session cookie for context /foo with
	 * requests intended for context /foobar.
	 */
	private boolean sessionCookiePathUsesTrailingSlash = false;
	/**
	 * The Jar scanner to use to search for Jars that might contain
	 * configuration information such as TLDs or web-fragment.xml files.
	 */
	private JarScanner jarScanner = null;
	/**
	 * Enables the RMI Target memory leak detection to be controlled. This is
	 * necessary since the detection can only work on Java 9 if some of the
	 * modularity checks are disabled.
	 */
	private boolean clearReferencesRmiTargets = true;
	/**
	 * Should Tomcat attempt to null out any static or final fields from loaded
	 * classes when a web application is stopped as a work around for apparent
	 * garbage collection bugs and application coding errors? There have been
	 * some issues reported with log4j when this option is true. Applications
	 * without memory leaks using recent JVMs should operate correctly with this
	 * option set to <code>false</code>. If not specified, the default value of
	 * <code>false</code> will be used.
	 *
	 * @deprecated This option will be removed in Tomcat 8.5
	 */
	@Deprecated
	private boolean clearReferencesStatic = false;
	/**
	 * Should Tomcat attempt to terminate threads that have been started by the
	 * web application? Stopping threads is performed via the deprecated (for
	 * good reason) <code>Thread.stop()</code> method and is likely to result in
	 * instability. As such, enabling this should be viewed as an option of last
	 * resort in a development environment and is not recommended in a
	 * production environment. If not specified, the default value of
	 * <code>false</code> will be used.
	 */
	private boolean clearReferencesStopThreads = false;
	/**
	 * Should Tomcat attempt to terminate any {@link java.util.TimerThread}s
	 * that have been started by the web application? If not specified, the
	 * default value of <code>false</code> will be used.
	 */
	private boolean clearReferencesStopTimerThreads = false;
	/**
	 * If an HttpClient keep-alive timer thread has been started by this web
	 * application and is still running, should Tomcat change the context class
	 * loader from the current {@link WebappClassLoaderBase} to
	 * {@link WebappClassLoaderBase#parent} to prevent a memory leak? Note that
	 * the keep-alive timer thread will stop on its own once the keep-alives all
	 * expire however, on a busy system that might not happen for some time.
	 */
	private boolean clearReferencesHttpClientKeepAliveThread = true;
	/**
	 * Should Tomcat renew the threads of the thread pool when the application
	 * is stopped to avoid memory leaks because of uncleaned ThreadLocal
	 * variables. This also requires that the threadRenewalDelay property of the
	 * StandardThreadExecutor of ThreadPoolExecutor be set to a positive value.
	 */
	private boolean renewThreadsWhenStoppingContext = true;
	/**
	 * Should the effective web.xml be logged when the context starts?
	 */
	private boolean logEffectiveWebXml = false;
	private int effectiveMajorVersion = 3;
	private int effectiveMinorVersion = 0;
	private JspConfigDescriptor jspConfigDescriptor =
			new ApplicationJspConfigDescriptor();
	private Set<String> resourceOnlyServlets = new HashSet<String>();
	private String webappVersion = "";
	private boolean addWebinfClassesResources = false;
	private boolean fireRequestListenersOnForwards = false;
	/**
	 * Servlets created via {@link ApplicationContext#createServlet(Class)} for
	 * tracking purposes.
	 */
	private Set<Servlet> createdServlets = new HashSet<Servlet>();
	private boolean preemptiveAuthentication = false;
	private boolean sendRedirectBody = false;
	private boolean jndiExceptionOnFailedWrite = true;
	private Map<String, String> postConstructMethods =
			new HashMap<String, String>();
	private Map<String, String> preDestroyMethods =
			new HashMap<String, String>();
	private String containerSciFilter;
	private Boolean failCtxIfServletStartFails;
	private boolean validateClientProvidedNewSessionId = true;
	private boolean mapperContextRootRedirectEnabled = true;
	private boolean mapperDirectoryRedirectEnabled = false;
	private boolean useRelativeRedirects = !Globals.STRICT_SERVLET_COMPLIANCE;
	private boolean dispatchersUseEncodedPaths = true;
	private MBeanNotificationInfo[] notificationInfo;


	// ----------------------------------------------------- Context Properties
	/**
	 * The J2EE Server ObjectName this module is deployed on.
	 */
	private String server = null;
	/**
	 * The Java virtual machines on which this module is running.
	 */
	private String[] javaVMs = null;

	/**
	 * Create a new StandardContext component with the default basic Valve.
	 */
	public StandardContext() {

		super();
		pipeline.setBasic(new StandardContextValve());
		broadcaster = new NotificationBroadcasterSupport();
		// Set defaults
		if (!Globals.STRICT_SERVLET_COMPLIANCE) {
			// Strict servlet compliance requires all extension mapped servlets
			// to be checked against welcome files
			resourceOnlyServlets.add("jsp");
		}
	}

	/**
	 * {@inheritDoc}
	 * <p>
	 * The default value for this implementation is {@code true}.
	 */
	@Override
	public boolean getDispatchersUseEncodedPaths() {
		return dispatchersUseEncodedPaths;
	}

	@Override
	public void setDispatchersUseEncodedPaths(boolean dispatchersUseEncodedPaths) {
		this.dispatchersUseEncodedPaths = dispatchersUseEncodedPaths;
	}

	/**
	 * {@inheritDoc}
	 * <p>
	 * The default value for this implementation is {@code true}.
	 */
	@Override
	public boolean getUseRelativeRedirects() {
		return useRelativeRedirects;
	}

	@Override
	public void setUseRelativeRedirects(boolean useRelativeRedirects) {
		this.useRelativeRedirects = useRelativeRedirects;
	}

	/**
	 * {@inheritDoc}
	 * <p>
	 * The default value for this implementation is {@code false}.
	 */
	@Override
	public boolean getMapperContextRootRedirectEnabled() {
		return mapperContextRootRedirectEnabled;
	}

	@Override
	public void setMapperContextRootRedirectEnabled(boolean mapperContextRootRedirectEnabled) {
		this.mapperContextRootRedirectEnabled = mapperContextRootRedirectEnabled;
	}

	/**
	 * {@inheritDoc}
	 * <p>
	 * The default value for this implementation is {@code false}.
	 */
	@Override
	public boolean getMapperDirectoryRedirectEnabled() {
		return mapperDirectoryRedirectEnabled;
	}

	@Override
	public void setMapperDirectoryRedirectEnabled(boolean mapperDirectoryRedirectEnabled) {
		this.mapperDirectoryRedirectEnabled = mapperDirectoryRedirectEnabled;
	}

	/**
	 * {@inheritDoc}
	 * <p>
	 * The default value for this implementation is {@code true}.
	 */
	@Override
	public boolean getValidateClientProvidedNewSessionId() {
		return validateClientProvidedNewSessionId;
	}

	@Override
	public void setValidateClientProvidedNewSessionId(boolean validateClientProvidedNewSessionId) {
		this.validateClientProvidedNewSessionId = validateClientProvidedNewSessionId;
	}

	@Override
	public String getContainerSciFilter() {
		return containerSciFilter;
	}

	@Override
	public void setContainerSciFilter(String containerSciFilter) {
		this.containerSciFilter = containerSciFilter;
	}

	@Override
	public boolean getSendRedirectBody() {
		return sendRedirectBody;
	}

	@Override
	public void setSendRedirectBody(boolean sendRedirectBody) {
		this.sendRedirectBody = sendRedirectBody;
	}

	@Override
	public boolean getPreemptiveAuthentication() {
		return preemptiveAuthentication;
	}

	@Override
	public void setPreemptiveAuthentication(boolean preemptiveAuthentication) {
		this.preemptiveAuthentication = preemptiveAuthentication;
	}

	@Override
	public boolean getFireRequestListenersOnForwards() {
		return fireRequestListenersOnForwards;
	}

	@Override
	public void setFireRequestListenersOnForwards(boolean enable) {
		fireRequestListenersOnForwards = enable;
	}

	public boolean getAddWebinfClassesResources() {
		return addWebinfClassesResources;
	}

	public void setAddWebinfClassesResources(
			boolean addWebinfClassesResources) {
		this.addWebinfClassesResources = addWebinfClassesResources;
	}

	@Override
	public String getWebappVersion() {
		return webappVersion;
	}

	@Override
	public void setWebappVersion(String webappVersion) {
		if (null == webappVersion) {
			this.webappVersion = "";
		} else {
			this.webappVersion = webappVersion;
		}
	}

	@Override
	public String getBaseName() {
		return new ContextName(path, webappVersion).getBaseName();
	}

	@Override
	public String getResourceOnlyServlets() {
		StringBuilder result = new StringBuilder();
		boolean first = true;
		for (String servletName : resourceOnlyServlets) {
			if (first) {
				first = false;
			} else {
				result.append(',');
			}
			result.append(servletName);
		}
		return result.toString();
	}

	@Override
	public void setResourceOnlyServlets(String resourceOnlyServlets) {
		this.resourceOnlyServlets.clear();
		if (resourceOnlyServlets == null) {
			return;
		}
		for (String servletName : resourceOnlyServlets.split(",")) {
			servletName = servletName.trim();
			if (servletName.length() > 0) {
				this.resourceOnlyServlets.add(servletName);
			}
		}
	}

	@Override
	public boolean isResourceOnlyServlet(String servletName) {
		return resourceOnlyServlets.contains(servletName);
	}

	@Override
	public int getEffectiveMajorVersion() {
		return effectiveMajorVersion;
	}

	@Override
	public void setEffectiveMajorVersion(int effectiveMajorVersion) {
		this.effectiveMajorVersion = effectiveMajorVersion;
	}

	@Override
	public int getEffectiveMinorVersion() {
		return effectiveMinorVersion;
	}

	@Override
	public void setEffectiveMinorVersion(int effectiveMinorVersion) {
		this.effectiveMinorVersion = effectiveMinorVersion;
	}

	@Override
	public boolean getLogEffectiveWebXml() {
		return logEffectiveWebXml;
	}

	@Override
	public void setLogEffectiveWebXml(boolean logEffectiveWebXml) {
		this.logEffectiveWebXml = logEffectiveWebXml;
	}

	@Override
	public Authenticator getAuthenticator() {
		if (this instanceof Authenticator)
			return (Authenticator) this;

		Pipeline pipeline = getPipeline();
		if (pipeline != null) {
			Valve basic = pipeline.getBasic();
			if ((basic != null) && (basic instanceof Authenticator))
				return (Authenticator) basic;
			Valve valves[] = pipeline.getValves();
			for (int i = 0; i < valves.length; i++) {
				if (valves[i] instanceof Authenticator)
					return (Authenticator) valves[i];
			}
		}
		return null;
	}

	@Override
	public JarScanner getJarScanner() {
		if (jarScanner == null) {
			jarScanner = new StandardJarScanner();
		}
		return jarScanner;
	}

	@Override
	public void setJarScanner(JarScanner jarScanner) {
		this.jarScanner = jarScanner;
	}

	@Override
	public InstanceManager getInstanceManager() {
		return instanceManager;
	}

	@Override
	public void setInstanceManager(InstanceManager instanceManager) {
		this.instanceManager = instanceManager;
	}

	@Override
	public String getEncodedPath() {
		return encodedPath;
	}

	/**
	 * Is caching allowed ?
	 */
	public boolean isCachingAllowed() {
		return cachingAllowed;
	}

	/**
	 * Set caching allowed flag.
	 */
	public void setCachingAllowed(boolean cachingAllowed) {
		this.cachingAllowed = cachingAllowed;
	}

	/**
	 * Is linking allowed.
	 */
	public boolean isAllowLinking() {
		return allowLinking;
	}

	/**
	 * Set allow linking.
	 */
	public void setAllowLinking(boolean allowLinking) {
		this.allowLinking = allowLinking;
	}

	/**
	 * Returns <code>true</code> if requests mapped to servlets without
	 * "multipart config" to parse multipart/form-data requests anyway.
	 *
	 * @return <code>true</code> if requests mapped to servlets without
	 * "multipart config" to parse multipart/form-data requests,
	 * <code>false</code> otherwise.
	 */
	@Override
	public boolean getAllowCasualMultipartParsing() {
		return this.allowCasualMultipartParsing;
	}

	/**
	 * Set to <code>true</code> to allow requests mapped to servlets that
	 * do not explicitly declare @MultipartConfig or have
	 * &lt;multipart-config&gt; specified in web.xml to parse
	 * multipart/form-data requests.
	 *
	 * @param allowCasualMultipartParsing <code>true</code> to allow such
	 *                                    casual parsing, <code>false</code> otherwise.
	 */
	@Override
	public void setAllowCasualMultipartParsing(
			boolean allowCasualMultipartParsing) {
		this.allowCasualMultipartParsing = allowCasualMultipartParsing;
	}

	/**
	 * Returns <code>true</code> if remaining request data will be read
	 * (swallowed) even the request violates a data size constraint.
	 *
	 * @return <code>true</code> if data will be swallowed (default),
	 * <code>false</code> otherwise.
	 */
	@Override
	public boolean getSwallowAbortedUploads() {
		return this.swallowAbortedUploads;
	}

	/**
	 * Set to <code>false</code> to disable request data swallowing
	 * after an upload was aborted due to size constraints.
	 *
	 * @param swallowAbortedUploads <code>false</code> to disable
	 *                              swallowing, <code>true</code> otherwise (default).
	 */
	@Override
	public void setSwallowAbortedUploads(boolean swallowAbortedUploads) {
		this.swallowAbortedUploads = swallowAbortedUploads;
	}

	/**
	 * Get cache TTL.
	 */
	public int getCacheTTL() {
		return cacheTTL;
	}

	/**
	 * Set cache TTL.
	 */
	public void setCacheTTL(int cacheTTL) {
		this.cacheTTL = cacheTTL;
	}

	/**
	 * Return the maximum size of the cache in KB.
	 */
	public int getCacheMaxSize() {
		return cacheMaxSize;
	}

	/**
	 * Set the maximum size of the cache in KB.
	 */
	public void setCacheMaxSize(int cacheMaxSize) {
		this.cacheMaxSize = cacheMaxSize;
	}

	/**
	 * Return the maximum size of objects to be cached in KB.
	 */
	public int getCacheObjectMaxSize() {
		return cacheObjectMaxSize;
	}

	/**
	 * Set the maximum size of objects to be placed the cache in KB.
	 */
	public void setCacheObjectMaxSize(int cacheObjectMaxSize) {
		this.cacheObjectMaxSize = cacheObjectMaxSize;
	}

	/**
	 * Return the list of resource aliases.
	 */
	public String getAliases() {
		return this.aliases;
	}

	/**
	 * Set the current alias configuration. The list of aliases should be of the
	 * form "/aliasPath1=docBase1,/aliasPath2=docBase2" where aliasPathN must
	 * include a leading '/' and docBaseN must be an absolute path to either a
	 * .war file or a directory.
	 */
	public void setAliases(String aliases) {
		this.aliases = aliases;
	}

	/**
	 * Add a URL for a JAR that contains static resources in a
	 * META-INF/resources directory that should be included in the static
	 * resources for this context.
	 */
	@Override
	public void addResourceJarUrl(URL url) {
		if (webappResources instanceof BaseDirContext) {
			((BaseDirContext) webappResources).addResourcesJar(url);
		} else {
			log.error(sm.getString("standardContext.noResourceJar", url,
					getName()));
		}
	}

	/**
	 * Add a URL for a JAR that contains static resources in a
	 * META-INF/resources directory that should be included in the static
	 * resources for this context.
	 */
	public void addResourcesDirContext(DirContext altDirContext) {
		if (webappResources instanceof BaseDirContext) {
			((BaseDirContext) webappResources).addAltDirContext(altDirContext);
		} else {
			log.error(sm.getString("standardContext.noResourceJar", altDirContext,
					getName()));
		}
	}

	/**
	 * Add a ServletContainerInitializer instance to this web application.
	 *
	 * @param sci     The instance to add
	 * @param classes The classes in which the initializer expressed an
	 *                interest
	 */
	@Override
	public void addServletContainerInitializer(
			ServletContainerInitializer sci, Set<Class<?>> classes) {
		initializers.put(sci, classes);
	}

	/**
	 * Return the "follow standard delegation model" flag used to configure
	 * our ClassLoader.
	 */
	public boolean getDelegate() {

		return (this.delegate);

	}

	/**
	 * Set the "follow standard delegation model" flag used to configure
	 * our ClassLoader.
	 *
	 * @param delegate The new flag
	 */
	public void setDelegate(boolean delegate) {

		boolean oldDelegate = this.delegate;
		this.delegate = delegate;
		support.firePropertyChange("delegate", oldDelegate,
				this.delegate);

	}

	/**
	 * Returns true if the internal naming support is used.
	 */
	public boolean isUseNaming() {

		return (useNaming);

	}

	/**
	 * Enables or disables naming.
	 */
	public void setUseNaming(boolean useNaming) {
		this.useNaming = useNaming;
	}

	/**
	 * Returns true if the resources associated with this context are
	 * filesystem based.
	 */
	@Deprecated
	public boolean isFilesystemBased() {

		return (filesystemBased);

	}

	/**
	 * Return the set of initialized application event listener objects,
	 * in the order they were specified in the web application deployment
	 * descriptor, for this application.
	 *
	 * @throws IllegalStateException if this method is called before
	 *                               this application has started, or after it has been stopped
	 */
	@Override
	public Object[] getApplicationEventListeners() {
		return (applicationEventListenersObjects);
	}

	/**
	 * Store the set of initialized application event listener objects,
	 * in the order they were specified in the web application deployment
	 * descriptor, for this application.
	 *
	 * @param listeners The set of instantiated listener objects.
	 */
	@Override
	public void setApplicationEventListeners(Object listeners[]) {
		applicationEventListenersObjects = listeners;
	}

	/**
	 * Add a listener to the end of the list of initialized application event
	 * listeners.
	 */
	public void addApplicationEventListener(Object listener) {
		int len = applicationEventListenersObjects.length;
		Object[] newListeners = Arrays.copyOf(applicationEventListenersObjects,
				len + 1);
		newListeners[len] = listener;
		applicationEventListenersObjects = newListeners;
	}

	/**
	 * Return the set of initialized application lifecycle listener objects,
	 * in the order they were specified in the web application deployment
	 * descriptor, for this application.
	 *
	 * @throws IllegalStateException if this method is called before
	 *                               this application has started, or after it has been stopped
	 */
	@Override
	public Object[] getApplicationLifecycleListeners() {
		return (applicationLifecycleListenersObjects);
	}

	/**
	 * Store the set of initialized application lifecycle listener objects,
	 * in the order they were specified in the web application deployment
	 * descriptor, for this application.
	 *
	 * @param listeners The set of instantiated listener objects.
	 */
	@Override
	public void setApplicationLifecycleListeners(Object listeners[]) {
		applicationLifecycleListenersObjects = listeners;
	}

	/**
	 * Add a listener to the end of the list of initialized application
	 * lifecycle listeners.
	 */
	public void addApplicationLifecycleListener(Object listener) {
		int len = applicationLifecycleListenersObjects.length;
		Object[] newListeners = Arrays.copyOf(
				applicationLifecycleListenersObjects, len + 1);
		newListeners[len] = listener;
		applicationLifecycleListenersObjects = newListeners;
	}

	/**
	 * Return the antiJARLocking flag for this Context.
	 */
	public boolean getAntiJARLocking() {

		return (this.antiJARLocking);

	}

	/**
	 * Set the antiJARLocking feature for this Context.
	 *
	 * @param antiJARLocking The new flag value
	 */
	public void setAntiJARLocking(boolean antiJARLocking) {

		boolean oldAntiJARLocking = this.antiJARLocking;
		this.antiJARLocking = antiJARLocking;
		support.firePropertyChange("antiJARLocking",
				oldAntiJARLocking,
				this.antiJARLocking);

	}

	/**
	 * Return the antiResourceLocking flag for this Context.
	 */
	public boolean getAntiResourceLocking() {

		return (this.antiResourceLocking);

	}

	/**
	 * Set the antiResourceLocking feature for this Context.
	 *
	 * @param antiResourceLocking The new flag value
	 */
	public void setAntiResourceLocking(boolean antiResourceLocking) {

		boolean oldAntiResourceLocking = this.antiResourceLocking;
		this.antiResourceLocking = antiResourceLocking;
		support.firePropertyChange("antiResourceLocking",
				oldAntiResourceLocking,
				this.antiResourceLocking);

	}

	/**
	 * Return the application available flag for this Context.
	 */
	@Override
	public boolean getAvailable() {

		// TODO Remove this method entirely
		return getState().isAvailable();

	}

	/**
	 * Return the Locale to character set mapper for this Context.
	 */
	@Override
	public CharsetMapper getCharsetMapper() {

		// Create a mapper the first time it is requested
		if (this.charsetMapper == null) {
			try {
				Class<?> clazz = Class.forName(charsetMapperClass);
				this.charsetMapper = (CharsetMapper) clazz.newInstance();
			} catch (Throwable t) {
				ExceptionUtils.handleThrowable(t);
				this.charsetMapper = new CharsetMapper();
			}
		}

		return (this.charsetMapper);

	}

	/**
	 * Set the Locale to character set mapper for this Context.
	 *
	 * @param mapper The new mapper
	 */
	@Override
	public void setCharsetMapper(CharsetMapper mapper) {

		CharsetMapper oldCharsetMapper = this.charsetMapper;
		this.charsetMapper = mapper;
		if (mapper != null)
			this.charsetMapperClass = mapper.getClass().getName();
		support.firePropertyChange("charsetMapper", oldCharsetMapper,
				this.charsetMapper);

	}

	@Override
	public String getCharset(Locale locale) {
		return getCharsetMapper().getCharset(locale);
	}

	/**
	 * Return the URL of the XML descriptor for this context.
	 */
	@Override
	public URL getConfigFile() {

		return (this.configFile);

	}

	/**
	 * Set the URL of the XML descriptor for this context.
	 *
	 * @param configFile The URL of the XML descriptor for this context.
	 */
	@Override
	public void setConfigFile(URL configFile) {

		this.configFile = configFile;
	}

	/**
	 * Return the "correctly configured" flag for this Context.
	 */
	@Override
	public boolean getConfigured() {

		return (this.configured);

	}

	/**
	 * Set the "correctly configured" flag for this Context.  This can be
	 * set to false by startup listeners that detect a fatal configuration
	 * error to avoid the application from being made available.
	 *
	 * @param configured The new correctly configured flag
	 */
	@Override
	public void setConfigured(boolean configured) {

		boolean oldConfigured = this.configured;
		this.configured = configured;
		support.firePropertyChange("configured",
				oldConfigured,
				this.configured);

	}

	/**
	 * Return the "use cookies for session ids" flag.
	 */
	@Override
	public boolean getCookies() {

		return (this.cookies);

	}

	/**
	 * Set the "use cookies for session ids" flag.
	 *
	 * @param cookies The new flag
	 */
	@Override
	public void setCookies(boolean cookies) {

		boolean oldCookies = this.cookies;
		this.cookies = cookies;
		support.firePropertyChange("cookies",
				oldCookies,
				this.cookies);

	}

	/**
	 * Gets the name to use for session cookies. Overrides any setting that
	 * may be specified by the application.
	 *
	 * @return The value of the default session cookie name or null if not
	 * specified
	 */
	@Override
	public String getSessionCookieName() {
		return sessionCookieName;
	}

	/**
	 * Sets the name to use for session cookies. Overrides any setting that
	 * may be specified by the application.
	 *
	 * @param sessionCookieName The name to use
	 */
	@Override
	public void setSessionCookieName(String sessionCookieName) {
		String oldSessionCookieName = this.sessionCookieName;
		this.sessionCookieName = sessionCookieName;
		support.firePropertyChange("sessionCookieName",
				oldSessionCookieName, sessionCookieName);
	}

	/**
	 * Gets the value of the use HttpOnly cookies for session cookies flag.
	 *
	 * @return <code>true</code> if the HttpOnly flag should be set on session
	 * cookies
	 */
	@Override
	public boolean getUseHttpOnly() {
		return useHttpOnly;
	}

	/**
	 * Sets the use HttpOnly cookies for session cookies flag.
	 *
	 * @param useHttpOnly Set to <code>true</code> to use HttpOnly cookies
	 *                    for session cookies
	 */
	@Override
	public void setUseHttpOnly(boolean useHttpOnly) {
		boolean oldUseHttpOnly = this.useHttpOnly;
		this.useHttpOnly = useHttpOnly;
		support.firePropertyChange("useHttpOnly",
				oldUseHttpOnly,
				this.useHttpOnly);
	}

	/**
	 * Gets the domain to use for session cookies. Overrides any setting that
	 * may be specified by the application.
	 *
	 * @return The value of the default session cookie domain or null if not
	 * specified
	 */
	@Override
	public String getSessionCookieDomain() {
		return sessionCookieDomain;
	}

	/**
	 * Sets the domain to use for session cookies. Overrides any setting that
	 * may be specified by the application.
	 *
	 * @param sessionCookieDomain The domain to use
	 */
	@Override
	public void setSessionCookieDomain(String sessionCookieDomain) {
		String oldSessionCookieDomain = this.sessionCookieDomain;
		this.sessionCookieDomain = sessionCookieDomain;
		support.firePropertyChange("sessionCookieDomain",
				oldSessionCookieDomain, sessionCookieDomain);
	}

	/**
	 * Gets the path to use for session cookies. Overrides any setting that
	 * may be specified by the application.
	 *
	 * @return The value of the default session cookie path or null if not
	 * specified
	 */
	@Override
	public String getSessionCookiePath() {
		return sessionCookiePath;
	}

	/**
	 * Sets the path to use for session cookies. Overrides any setting that
	 * may be specified by the application.
	 *
	 * @param sessionCookiePath The path to use
	 */
	@Override
	public void setSessionCookiePath(String sessionCookiePath) {
		String oldSessionCookiePath = this.sessionCookiePath;
		this.sessionCookiePath = sessionCookiePath;
		support.firePropertyChange("sessionCookiePath",
				oldSessionCookiePath, sessionCookiePath);
	}

	@Override
	public boolean getSessionCookiePathUsesTrailingSlash() {
		return sessionCookiePathUsesTrailingSlash;
	}

	@Override
	public void setSessionCookiePathUsesTrailingSlash(
			boolean sessionCookiePathUsesTrailingSlash) {
		this.sessionCookiePathUsesTrailingSlash =
				sessionCookiePathUsesTrailingSlash;
	}

	/**
	 * Return the "allow crossing servlet contexts" flag.
	 */
	@Override
	public boolean getCrossContext() {

		return (this.crossContext);

	}

	/**
	 * Set the "allow crossing servlet contexts" flag.
	 *
	 * @param crossContext The new cross contexts flag
	 */
	@Override
	public void setCrossContext(boolean crossContext) {

		boolean oldCrossContext = this.crossContext;
		this.crossContext = crossContext;
		support.firePropertyChange("crossContext",
				oldCrossContext,
				this.crossContext);

	}

	public String getDefaultContextXml() {
		return defaultContextXml;
	}

	/**
	 * Set the location of the default context xml that will be used.
	 * If not absolute, it'll be made relative to the engine's base dir
	 * ( which defaults to catalina.base system property ).
	 *
	 * @param defaultContextXml The default web xml
	 */
	public void setDefaultContextXml(String defaultContextXml) {
		this.defaultContextXml = defaultContextXml;
	}

	public String getDefaultWebXml() {
		return defaultWebXml;
	}

	/**
	 * Set the location of the default web xml that will be used.
	 * If not absolute, it'll be made relative to the engine's base dir
	 * ( which defaults to catalina.base system property ).
	 *
	 * @param defaultWebXml The default web xml
	 */
	public void setDefaultWebXml(String defaultWebXml) {
		this.defaultWebXml = defaultWebXml;
	}

	/**
	 * Gets the time (in milliseconds) it took to start this context.
	 *
	 * @return Time (in milliseconds) it took to start this context.
	 */
	public long getStartupTime() {
		return startupTime;
	}

	public void setStartupTime(long startupTime) {
		this.startupTime = startupTime;
	}

	public long getTldScanTime() {
		return tldScanTime;
	}

	public void setTldScanTime(long tldScanTime) {
		this.tldScanTime = tldScanTime;
	}

	/**
	 * Return the display name of this web application.
	 */
	@Override
	public String getDisplayName() {

		return (this.displayName);

	}

	/**
	 * Set the display name of this web application.
	 *
	 * @param displayName The new display name
	 */
	@Override
	public void setDisplayName(String displayName) {

		String oldDisplayName = this.displayName;
		this.displayName = displayName;
		support.firePropertyChange("displayName", oldDisplayName,
				this.displayName);
	}

	/**
	 * Return the alternate Deployment Descriptor name.
	 */
	@Override
	public String getAltDDName() {
		return altDDName;
	}

	/**
	 * Set an alternate Deployment Descriptor name.
	 */
	@Override
	public void setAltDDName(String altDDName) {
		this.altDDName = altDDName;
		if (context != null) {
			context.setAttribute(Globals.ALT_DD_ATTR, altDDName);
		}
	}

	/**
	 * Return the compiler classpath.
	 */
	@Deprecated
	public String getCompilerClasspath() {
		return compilerClasspath;
	}

	/**
	 * Set the compiler classpath.
	 */
	@Deprecated
	public void setCompilerClasspath(String compilerClasspath) {
		this.compilerClasspath = compilerClasspath;
	}

	/**
	 * Return the distributable flag for this web application.
	 */
	@Override
	public boolean getDistributable() {

		return (this.distributable);

	}

	/**
	 * Set the distributable flag for this web application.
	 *
	 * @param distributable The new distributable flag
	 */
	@Override
	public void setDistributable(boolean distributable) {
		boolean oldDistributable = this.distributable;
		this.distributable = distributable;
		support.firePropertyChange("distributable",
				oldDistributable,
				this.distributable);
	}

	/**
	 * Return the document root for this Context.  This can be an absolute
	 * pathname, a relative pathname, or a URL.
	 */
	@Override
	public String getDocBase() {

		return (this.docBase);

	}

	/**
	 * Set the document root for this Context.  This can be an absolute
	 * pathname, a relative pathname, or a URL.
	 *
	 * @param docBase The new document root
	 */
	@Override
	public void setDocBase(String docBase) {

		this.docBase = docBase;

	}

	/**
	 * Return descriptive information about this Container implementation and
	 * the corresponding version number, in the format
	 * <code>&lt;description&gt;/&lt;version&gt;</code>.
	 */
	@Override
	public String getInfo() {

		return (info);

	}

	public String getJ2EEApplication() {
		return j2EEApplication;
	}

	public void setJ2EEApplication(String j2EEApplication) {
		this.j2EEApplication = j2EEApplication;
	}

	public String getJ2EEServer() {
		return j2EEServer;
	}

	public void setJ2EEServer(String j2EEServer) {
		this.j2EEServer = j2EEServer;
	}

	/**
	 * Return the boolean on the annotations parsing.
	 */
	@Override
	public boolean getIgnoreAnnotations() {
		return this.ignoreAnnotations;
	}

	/**
	 * Set the boolean on the annotations parsing for this web
	 * application.
	 *
	 * @param ignoreAnnotations The boolean on the annotations parsing
	 */
	@Override
	public void setIgnoreAnnotations(boolean ignoreAnnotations) {
		boolean oldIgnoreAnnotations = this.ignoreAnnotations;
		this.ignoreAnnotations = ignoreAnnotations;
		support.firePropertyChange("ignoreAnnotations", oldIgnoreAnnotations,
				this.ignoreAnnotations);
	}

	/**
	 * Return the login configuration descriptor for this web application.
	 */
	@Override
	public LoginConfig getLoginConfig() {

		return (this.loginConfig);

	}

	/**
	 * Set the login configuration descriptor for this web application.
	 *
	 * @param config The new login configuration
	 */
	@Override
	public void setLoginConfig(LoginConfig config) {

		// Validate the incoming property value
		if (config == null)
			throw new IllegalArgumentException
					(sm.getString("standardContext.loginConfig.required"));
		String loginPage = config.getLoginPage();
		if ((loginPage != null) && !loginPage.startsWith("/")) {
			if (isServlet22()) {
				if (log.isDebugEnabled())
					log.debug(sm.getString("standardContext.loginConfig.loginWarning",
							loginPage));
				config.setLoginPage("/" + loginPage);
			} else {
				throw new IllegalArgumentException
						(sm.getString("standardContext.loginConfig.loginPage",
								loginPage));
			}
		}
		String errorPage = config.getErrorPage();
		if ((errorPage != null) && !errorPage.startsWith("/")) {
			if (isServlet22()) {
				if (log.isDebugEnabled())
					log.debug(sm.getString("standardContext.loginConfig.errorWarning",
							errorPage));
				config.setErrorPage("/" + errorPage);
			} else {
				throw new IllegalArgumentException
						(sm.getString("standardContext.loginConfig.errorPage",
								errorPage));
			}
		}

		// Process the property setting change
		LoginConfig oldLoginConfig = this.loginConfig;
		this.loginConfig = config;
		support.firePropertyChange("loginConfig",
				oldLoginConfig, this.loginConfig);

	}

	/**
	 * Get the mapper associated with the context.
	 */
	@Override
	public org.apache.tomcat.util.http.mapper.Mapper getMapper() {
		return (mapper);
	}

	/**
	 * Return the naming resources associated with this web application.
	 */
	@Override
	public NamingResources getNamingResources() {

		if (namingResources == null) {
			setNamingResources(new NamingResources());
		}
		return (namingResources);

	}

	/**
	 * Set the naming resources for this web application.
	 *
	 * @param namingResources The new naming resources
	 */
	@Override
	public void setNamingResources(NamingResources namingResources) {

		// Process the property setting change
		NamingResources oldNamingResources = this.namingResources;
		this.namingResources = namingResources;
		if (namingResources != null) {
			namingResources.setContainer(this);
		}
		support.firePropertyChange("namingResources",
				oldNamingResources, this.namingResources);

		if (getState() == LifecycleState.NEW ||
				getState() == LifecycleState.INITIALIZING ||
				getState() == LifecycleState.INITIALIZED) {
			// NEW will occur if Context is defined in server.xml
			// At this point getObjectKeyPropertiesNameOnly() will trigger an
			// NPE.
			// INITIALIZED will occur if the Context is defined in a context.xml
			// file
			// If started now, a second start will be attempted when the context
			// starts

			// In both cases, return and let context init the namingResources
			// when it starts
			return;
		}

		if (oldNamingResources != null) {
			try {
				oldNamingResources.stop();
				oldNamingResources.destroy();
			} catch (LifecycleException e) {
				log.warn("standardContext.namingResource.destroy.fail", e);
			}
		}
		if (namingResources != null) {
			try {
				namingResources.init();
				namingResources.start();
			} catch (LifecycleException e) {
				log.warn("standardContext.namingResource.init.fail", e);
			}
		}
	}

	/**
	 * Return the context path for this Context.
	 */
	@Override
	public String getPath() {
		return (path);
	}

	/**
	 * Set the context path for this Context.
	 *
	 * @param path The new context path
	 */
	@Override
	public void setPath(String path) {
		boolean invalid = false;
		if (path == null || path.equals("/")) {
			invalid = true;
			this.path = "";
		} else if ("".equals(path) || path.startsWith("/")) {
			this.path = path;
		} else {
			invalid = true;
			this.path = "/" + path;
		}
		if (this.path.endsWith("/")) {
			invalid = true;
			this.path = this.path.substring(0, this.path.length() - 1);
		}
		if (invalid) {
			log.warn(sm.getString(
					"standardContext.pathInvalid", path, this.path));
		}
		encodedPath = urlEncoder.encode(this.path, "UTF-8");
		if (getName() == null) {
			setName(this.path);
		}
	}

	/**
	 * Return the public identifier of the deployment descriptor DTD that is
	 * currently being parsed.
	 */
	@Override
	public String getPublicId() {

		return (this.publicId);

	}

	/**
	 * Set the public identifier of the deployment descriptor DTD that is
	 * currently being parsed.
	 *
	 * @param publicId The public identifier
	 */
	@Override
	public void setPublicId(String publicId) {

		if (log.isDebugEnabled())
			log.debug("Setting deployment descriptor public ID to '" +
					publicId + "'");

		String oldPublicId = this.publicId;
		this.publicId = publicId;
		support.firePropertyChange("publicId", oldPublicId, publicId);

	}

	/**
	 * Return the reloadable flag for this web application.
	 */
	@Override
	public boolean getReloadable() {

		return (this.reloadable);

	}

	/**
	 * Set the reloadable flag for this web application.
	 *
	 * @param reloadable The new reloadable flag
	 */
	@Override
	public void setReloadable(boolean reloadable) {

		boolean oldReloadable = this.reloadable;
		this.reloadable = reloadable;
		support.firePropertyChange("reloadable",
				oldReloadable,
				this.reloadable);

	}

	/**
	 * Return the default context override flag for this web application.
	 */
	@Override
	public boolean getOverride() {

		return (this.override);

	}

	/**
	 * Set the default context override flag for this web application.
	 *
	 * @param override The new override flag
	 */
	@Override
	public void setOverride(boolean override) {

		boolean oldOverride = this.override;
		this.override = override;
		support.firePropertyChange("override",
				oldOverride,
				this.override);

	}

	/**
	 * Return the original document root for this Context.  This can be an absolute
	 * pathname, a relative pathname, or a URL.
	 * Is only set as deployment has change docRoot!
	 */
	public String getOriginalDocBase() {

		return (this.originalDocBase);

	}

	/**
	 * Set the original document root for this Context.  This can be an absolute
	 * pathname, a relative pathname, or a URL.
	 *
	 * @param docBase The original document root
	 */
	public void setOriginalDocBase(String docBase) {

		this.originalDocBase = docBase;
	}

	/**
	 * Return the parent class loader (if any) for this web application.
	 * This call is meaningful only <strong>after</strong> a Loader has
	 * been configured.
	 */
	@Override
	public ClassLoader getParentClassLoader() {
		if (parentClassLoader != null)
			return (parentClassLoader);
		if (getPrivileged()) {
			return this.getClass().getClassLoader();
		} else if (parent != null) {
			return (parent.getParentClassLoader());
		}
		return (ClassLoader.getSystemClassLoader());
	}

	/**
	 * Return the privileged flag for this web application.
	 */
	@Override
	public boolean getPrivileged() {

		return (this.privileged);

	}

	/**
	 * Set the privileged flag for this web application.
	 *
	 * @param privileged The new privileged flag
	 */
	@Override
	public void setPrivileged(boolean privileged) {

		boolean oldPrivileged = this.privileged;
		this.privileged = privileged;
		support.firePropertyChange("privileged",
				oldPrivileged,
				this.privileged);

	}

	/**
	 * Return the "replace welcome files" property.
	 */
	@Deprecated
	public boolean isReplaceWelcomeFiles() {

		return (this.replaceWelcomeFiles);

	}

	/**
	 * Set the "replace welcome files" property.
	 *
	 * @param replaceWelcomeFiles The new property value
	 */
	public void setReplaceWelcomeFiles(boolean replaceWelcomeFiles) {

		boolean oldReplaceWelcomeFiles = this.replaceWelcomeFiles;
		this.replaceWelcomeFiles = replaceWelcomeFiles;
		support.firePropertyChange("replaceWelcomeFiles",
				oldReplaceWelcomeFiles,
				this.replaceWelcomeFiles);

	}

	/**
	 * Return the servlet context for which this Context is a facade.
	 */
	@Override
	public ServletContext getServletContext() {

		if (context == null) {
			context = new ApplicationContext(this);
			if (altDDName != null)
				context.setAttribute(Globals.ALT_DD_ATTR, altDDName);
		}
		return (context.getFacade());

	}

	/**
	 * Return the default session timeout (in minutes) for this
	 * web application.
	 */
	@Override
	public int getSessionTimeout() {

		return (this.sessionTimeout);

	}

	/**
	 * Set the default session timeout (in minutes) for this
	 * web application.
	 *
	 * @param timeout The new default session timeout
	 */
	@Override
	public void setSessionTimeout(int timeout) {

		int oldSessionTimeout = this.sessionTimeout;
	    /*
         * SRV.13.4 ("Deployment Descriptor"):
         * If the timeout is 0 or less, the container ensures the default
         * behaviour of sessions is never to time out.
         */
		this.sessionTimeout = (timeout == 0) ? -1 : timeout;
		support.firePropertyChange("sessionTimeout",
				oldSessionTimeout,
				this.sessionTimeout);

	}

	/**
	 * Return the value of the swallowOutput flag.
	 */
	@Override
	public boolean getSwallowOutput() {

		return (this.swallowOutput);

	}

	/**
	 * Set the value of the swallowOutput flag. If set to true, the system.out
	 * and system.err will be redirected to the logger during a servlet
	 * execution.
	 *
	 * @param swallowOutput The new value
	 */
	@Override
	public void setSwallowOutput(boolean swallowOutput) {

		boolean oldSwallowOutput = this.swallowOutput;
		this.swallowOutput = swallowOutput;
		support.firePropertyChange("swallowOutput",
				oldSwallowOutput,
				this.swallowOutput);

	}

	/**
	 * Return the value of the unloadDelay flag.
	 */
	public long getUnloadDelay() {

		return (this.unloadDelay);

	}

	/**
	 * Set the value of the unloadDelay flag, which represents the amount
	 * of ms that the container will wait when unloading servlets.
	 * Setting this to a small value may cause more requests to fail
	 * to complete when stopping a web application.
	 *
	 * @param unloadDelay The new value
	 */
	public void setUnloadDelay(long unloadDelay) {

		long oldUnloadDelay = this.unloadDelay;
		this.unloadDelay = unloadDelay;
		support.firePropertyChange("unloadDelay",
				Long.valueOf(oldUnloadDelay),
				Long.valueOf(this.unloadDelay));

	}

	/**
	 * Unpack WAR flag accessor.
	 */
	public boolean getUnpackWAR() {

		return (unpackWAR);

	}

	/**
	 * Unpack WAR flag mutator.
	 */
	public void setUnpackWAR(boolean unpackWAR) {

		this.unpackWAR = unpackWAR;

	}

	public boolean getCopyXML() {
		return copyXML;
	}

	public void setCopyXML(boolean copyXML) {
		this.copyXML = copyXML;
	}

	/**
	 * Return the Java class name of the Wrapper implementation used
	 * for servlets registered in this Context.
	 */
	@Override
	public String getWrapperClass() {

		return (this.wrapperClassName);

	}


	// ------------------------------------------------------ Public Properties

	/**
	 * Set the Java class name of the Wrapper implementation used
	 * for servlets registered in this Context.
	 *
	 * @param wrapperClassName The new wrapper class name
	 * @throws IllegalArgumentException if the specified wrapper class
	 *                                  cannot be found or is not a subclass of StandardWrapper
	 */
	@Override
	public void setWrapperClass(String wrapperClassName) {

		this.wrapperClassName = wrapperClassName;

		try {
			wrapperClass = Class.forName(wrapperClassName);
			if (!StandardWrapper.class.isAssignableFrom(wrapperClass)) {
				throw new IllegalArgumentException(
						sm.getString("standardContext.invalidWrapperClass",
								wrapperClassName));
			}
		} catch (ClassNotFoundException cnfe) {
			throw new IllegalArgumentException(cnfe.getMessage());
		}
	}

	/**
	 * Set the resources DirContext object with which this Container is
	 * associated.
	 *
	 * @param resources The newly associated DirContext
	 */
	@Override
	public synchronized void setResources(DirContext resources) {

		if (getState().isAvailable()) {
			throw new IllegalStateException
					(sm.getString("standardContext.resourcesStart"));
		}

		DirContext oldResources = this.webappResources;
		if (oldResources == resources)
			return;

		if (resources instanceof BaseDirContext) {
			// Caching
			((BaseDirContext) resources).setCached(isCachingAllowed());
			((BaseDirContext) resources).setCacheTTL(getCacheTTL());
			((BaseDirContext) resources).setCacheMaxSize(getCacheMaxSize());
			((BaseDirContext) resources).setCacheObjectMaxSize(
					getCacheObjectMaxSize());
			// Alias support
			((BaseDirContext) resources).setAliases(getAliases());
		}
		if (resources instanceof FileDirContext) {
			filesystemBased = true;
			((FileDirContext) resources).setAllowLinking(isAllowLinking());
		}
		this.webappResources = resources;

		// The proxied resources will be refreshed on start
		this.resources = null;

		support.firePropertyChange("resources", oldResources,
				this.webappResources);

	}

	@Override
	public JspConfigDescriptor getJspConfigDescriptor() {
		return jspConfigDescriptor;
	}

	/**
	 * Returns whether or not an attempt to modify the JNDI context will trigger
	 * an exception or if the request will be ignored.
	 */
	public boolean getJndiExceptionOnFailedWrite() {
		return jndiExceptionOnFailedWrite;
	}

	/**
	 * Controls whether or not an attempt to modify the JNDI context will
	 * trigger an exception or if the request will be ignored.
	 *
	 * @param jndiExceptionOnFailedWrite
	 */
	public void setJndiExceptionOnFailedWrite(
			boolean jndiExceptionOnFailedWrite) {
		this.jndiExceptionOnFailedWrite = jndiExceptionOnFailedWrite;
	}

	/**
	 * Return the Locale to character set mapper class for this Context.
	 */
	public String getCharsetMapperClass() {

		return (this.charsetMapperClass);

	}

	/**
	 * Set the Locale to character set mapper class for this Context.
	 *
	 * @param mapper The new mapper class
	 */
	public void setCharsetMapperClass(String mapper) {

		String oldCharsetMapperClass = this.charsetMapperClass;
		this.charsetMapperClass = mapper;
		support.firePropertyChange("charsetMapperClass",
				oldCharsetMapperClass,
				this.charsetMapperClass);

	}

	/**
	 * Get the absolute path to the work dir.
	 * To avoid duplication.
	 *
	 * @return The work path
	 */
	public String getWorkPath() {
		if (getWorkDir() == null) {
			return null;
		}
		File workDir = new File(getWorkDir());
		if (!workDir.isAbsolute()) {
			File catalinaHome = engineBase();
			String catalinaHomePath = null;
			try {
				catalinaHomePath = catalinaHome.getCanonicalPath();
				workDir = new File(catalinaHomePath,
						getWorkDir());
			} catch (IOException e) {
				log.warn(sm.getString("standardContext.workPath", getName()),
						e);
			}
		}
		return workDir.getAbsolutePath();
	}

	/**
	 * Return the work directory for this Context.
	 */
	public String getWorkDir() {

		return (this.workDir);

	}

	/**
	 * Set the work directory for this Context.
	 *
	 * @param workDir The new work directory
	 */
	public void setWorkDir(String workDir) {

		this.workDir = workDir;

		if (getState().isAvailable()) {
			postWorkDirectory();
		}
	}

	public boolean getClearReferencesRmiTargets() {
		return this.clearReferencesRmiTargets;
	}

	public void setClearReferencesRmiTargets(boolean clearReferencesRmiTargets) {
		boolean oldClearReferencesRmiTargets = this.clearReferencesRmiTargets;
		this.clearReferencesRmiTargets = clearReferencesRmiTargets;
		support.firePropertyChange("clearReferencesRmiTargets",
				oldClearReferencesRmiTargets, this.clearReferencesRmiTargets);
	}

	/**
	 * Save config ?
	 */
	@Deprecated
	public boolean isSaveConfig() {
		return saveConfig;
	}

	/**
	 * Set save config flag.
	 */
	@Deprecated
	public void setSaveConfig(boolean saveConfig) {
		this.saveConfig = saveConfig;
	}

	/**
	 * Return the clearReferencesStatic flag for this Context.
	 *
	 * @deprecated This option will be removed in Tomcat 8.5
	 */
	@Deprecated
	public boolean getClearReferencesStatic() {

		return (this.clearReferencesStatic);

	}

	/**
	 * Set the clearReferencesStatic feature for this Context.
	 *
	 * @param clearReferencesStatic The new flag value
	 * @deprecated This option will be removed in Tomcat 8.5
	 */
	@Deprecated
	public void setClearReferencesStatic(boolean clearReferencesStatic) {

		boolean oldClearReferencesStatic = this.clearReferencesStatic;
		this.clearReferencesStatic = clearReferencesStatic;
		support.firePropertyChange("clearReferencesStatic",
				oldClearReferencesStatic,
				this.clearReferencesStatic);

	}

	/**
	 * Return the clearReferencesStopThreads flag for this Context.
	 */
	public boolean getClearReferencesStopThreads() {

		return (this.clearReferencesStopThreads);

	}

	/**
	 * Set the clearReferencesStopThreads feature for this Context.
	 *
	 * @param clearReferencesStopThreads The new flag value
	 */
	public void setClearReferencesStopThreads(
			boolean clearReferencesStopThreads) {

		boolean oldClearReferencesStopThreads = this.clearReferencesStopThreads;
		this.clearReferencesStopThreads = clearReferencesStopThreads;
		support.firePropertyChange("clearReferencesStopThreads",
				oldClearReferencesStopThreads,
				this.clearReferencesStopThreads);

	}

	/**
	 * Return the clearReferencesStopTimerThreads flag for this Context.
	 */
	public boolean getClearReferencesStopTimerThreads() {
		return (this.clearReferencesStopTimerThreads);
	}

	/**
	 * Set the clearReferencesStopTimerThreads feature for this Context.
	 *
	 * @param clearReferencesStopTimerThreads The new flag value
	 */
	public void setClearReferencesStopTimerThreads(
			boolean clearReferencesStopTimerThreads) {

		boolean oldClearReferencesStopTimerThreads =
				this.clearReferencesStopTimerThreads;
		this.clearReferencesStopTimerThreads = clearReferencesStopTimerThreads;
		support.firePropertyChange("clearReferencesStopTimerThreads",
				oldClearReferencesStopTimerThreads,
				this.clearReferencesStopTimerThreads);
	}

	/**
	 * Return the clearReferencesHttpClientKeepAliveThread flag for this
	 * Context.
	 */
	public boolean getClearReferencesHttpClientKeepAliveThread() {
		return (this.clearReferencesHttpClientKeepAliveThread);
	}

	/**
	 * Set the clearReferencesHttpClientKeepAliveThread feature for this
	 * Context.
	 *
	 * @param clearReferencesHttpClientKeepAliveThread The new flag value
	 */
	public void setClearReferencesHttpClientKeepAliveThread(
			boolean clearReferencesHttpClientKeepAliveThread) {
		this.clearReferencesHttpClientKeepAliveThread =
				clearReferencesHttpClientKeepAliveThread;
	}

	public boolean getRenewThreadsWhenStoppingContext() {
		return this.renewThreadsWhenStoppingContext;
	}

	public void setRenewThreadsWhenStoppingContext(
			boolean renewThreadsWhenStoppingContext) {
		boolean oldRenewThreadsWhenStoppingContext =
				this.renewThreadsWhenStoppingContext;
		this.renewThreadsWhenStoppingContext = renewThreadsWhenStoppingContext;
		support.firePropertyChange("renewThreadsWhenStoppingContext",
				oldRenewThreadsWhenStoppingContext,
				this.renewThreadsWhenStoppingContext);
	}

	// -------------------------------------------------------- Context Methods

	public Boolean getFailCtxIfServletStartFails() {
		return failCtxIfServletStartFails;
	}

	public void setFailCtxIfServletStartFails(
			Boolean failCtxIfServletStartFails) {
		Boolean oldFailCtxIfServletStartFails = this.failCtxIfServletStartFails;
		this.failCtxIfServletStartFails = failCtxIfServletStartFails;
		support.firePropertyChange("failCtxIfServletStartFails",
				oldFailCtxIfServletStartFails,
				failCtxIfServletStartFails);
	}

	protected boolean getComputedFailCtxIfServletStartFails() {
		if (failCtxIfServletStartFails != null) {
			return failCtxIfServletStartFails.booleanValue();
		}
		//else look at Host config
		if (getParent() instanceof StandardHost) {
			return ((StandardHost) getParent()).isFailCtxIfServletStartFails();
		}
		//else
		return false;
	}

	@Override
	public void addApplicationListener(String listener) {
		addApplicationListener(new ApplicationListener(listener, false));
	}

	@Override
	public void addApplicationListener(ApplicationListener listener) {

		synchronized (applicationListenersLock) {
			ApplicationListener results[] =
					new ApplicationListener[applicationListeners.length + 1];
			for (int i = 0; i < applicationListeners.length; i++) {
				if (listener.equals(applicationListeners[i])) {
					log.info(sm.getString("standardContext.duplicateListener",
							listener.getClassName()));
					return;
				}
				results[i] = applicationListeners[i];
			}
			results[applicationListeners.length] = listener;
			applicationListeners = results;
		}
		fireContainerEvent("addApplicationListener", listener);

		// FIXME - add instance if already started?

	}

	/**
	 * Add a new application parameter for this application.
	 *
	 * @param parameter The new application parameter
	 */
	@Override
	public void addApplicationParameter(ApplicationParameter parameter) {

		synchronized (applicationParametersLock) {
			String newName = parameter.getName();
			for (ApplicationParameter p : applicationParameters) {
				if (newName.equals(p.getName()) && !p.getOverride())
					return;
			}
			ApplicationParameter results[] = Arrays.copyOf(
					applicationParameters, applicationParameters.length + 1);
			results[applicationParameters.length] = parameter;
			applicationParameters = results;
		}
		fireContainerEvent("addApplicationParameter", parameter);

	}

	/**
	 * Add a child Container, only if the proposed child is an implementation
	 * of Wrapper.
	 *
	 * @param child Child container to be added
	 * @throws IllegalArgumentException if the proposed container is
	 *                                  not an implementation of Wrapper
	 */
	@Override
	public void addChild(Container child) {

		// Global JspServlet
		Wrapper oldJspServlet = null;

		if (!(child instanceof Wrapper)) {
			throw new IllegalArgumentException
					(sm.getString("standardContext.notWrapper"));
		}

		boolean isJspServlet = "jsp".equals(child.getName());

		// Allow webapp to override JspServlet inherited from global web.xml.
		if (isJspServlet) {
			oldJspServlet = (Wrapper) findChild("jsp");
			if (oldJspServlet != null) {
				removeChild(oldJspServlet);
			}
		}

		super.addChild(child);

		if (isJspServlet && oldJspServlet != null) {
            /*
             * The webapp-specific JspServlet inherits all the mappings
             * specified in the global web.xml, and may add additional ones.
             */
			String[] jspMappings = oldJspServlet.findMappings();
			for (int i = 0; jspMappings != null && i < jspMappings.length; i++) {
				addServletMapping(jspMappings[i], child.getName());
			}
		}
	}

	/**
	 * Add a security constraint to the set for this web application.
	 */
	@Override
	public void addConstraint(SecurityConstraint constraint) {

		// Validate the proposed constraint
		SecurityCollection collections[] = constraint.findCollections();
		for (int i = 0; i < collections.length; i++) {
			String patterns[] = collections[i].findPatterns();
			for (int j = 0; j < patterns.length; j++) {
				patterns[j] = adjustURLPattern(patterns[j]);
				if (!validateURLPattern(patterns[j]))
					throw new IllegalArgumentException
							(sm.getString
									("standardContext.securityConstraint.pattern",
											patterns[j]));
			}
			if (collections[i].findMethods().length > 0 &&
					collections[i].findOmittedMethods().length > 0) {
				throw new IllegalArgumentException(sm.getString(
						"standardContext.securityConstraint.mixHttpMethod"));
			}
		}

		// Add this constraint to the set for our web application
		synchronized (constraintsLock) {
			SecurityConstraint results[] =
					new SecurityConstraint[constraints.length + 1];
			for (int i = 0; i < constraints.length; i++)
				results[i] = constraints[i];
			results[constraints.length] = constraint;
			constraints = results;
		}

	}

	/**
	 * Add an error page for the specified error or Java exception.
	 *
	 * @param errorPage The error page definition to be added
	 */
	@Override
	public void addErrorPage(ErrorPage errorPage) {
		// Validate the input parameters
		if (errorPage == null)
			throw new IllegalArgumentException
					(sm.getString("standardContext.errorPage.required"));
		String location = errorPage.getLocation();
		if ((location != null) && !location.startsWith("/")) {
			if (isServlet22()) {
				if (log.isDebugEnabled())
					log.debug(sm.getString("standardContext.errorPage.warning",
							location));
				errorPage.setLocation("/" + location);
			} else {
				throw new IllegalArgumentException
						(sm.getString("standardContext.errorPage.error",
								location));
			}
		}

		// Add the specified error page to our internal collections
		String exceptionType = errorPage.getExceptionType();
		if (exceptionType != null) {
			synchronized (exceptionPages) {
				exceptionPages.put(exceptionType, errorPage);
			}
		} else {
			synchronized (statusPages) {
				if (errorPage.getErrorCode() == 200) {
					this.okErrorPage = errorPage;
				}
				statusPages.put(Integer.valueOf(errorPage.getErrorCode()),
						errorPage);
			}
		}
		fireContainerEvent("addErrorPage", errorPage);

	}

	/**
	 * Add a filter definition to this Context.
	 *
	 * @param filterDef The filter definition to be added
	 */
	@Override
	public void addFilterDef(FilterDef filterDef) {

		synchronized (filterDefs) {
			filterDefs.put(filterDef.getFilterName(), filterDef);
		}
		fireContainerEvent("addFilterDef", filterDef);

	}

	/**
	 * Add a filter mapping to this Context at the end of the current set
	 * of filter mappings.
	 *
	 * @param filterMap The filter mapping to be added
	 * @throws IllegalArgumentException if the specified filter name
	 *                                  does not match an existing filter definition, or the filter mapping
	 *                                  is malformed
	 */
	@Override
	public void addFilterMap(FilterMap filterMap) {
		validateFilterMap(filterMap);
		// Add this filter mapping to our registered set
		filterMaps.add(filterMap);
		fireContainerEvent("addFilterMap", filterMap);
	}

	/**
	 * Add a filter mapping to this Context before the mappings defined in the
	 * deployment descriptor but after any other mappings added via this method.
	 *
	 * @param filterMap The filter mapping to be added
	 * @throws IllegalArgumentException if the specified filter name
	 *                                  does not match an existing filter definition, or the filter mapping
	 *                                  is malformed
	 */
	@Override
	public void addFilterMapBefore(FilterMap filterMap) {
		validateFilterMap(filterMap);
		// Add this filter mapping to our registered set
		filterMaps.addBefore(filterMap);
		fireContainerEvent("addFilterMap", filterMap);
	}

	/**
	 * Validate the supplied FilterMap.
	 */
	private void validateFilterMap(FilterMap filterMap) {
		// Validate the proposed filter mapping
		String filterName = filterMap.getFilterName();
		String[] servletNames = filterMap.getServletNames();
		String[] urlPatterns = filterMap.getURLPatterns();
		if (findFilterDef(filterName) == null)
			throw new IllegalArgumentException
					(sm.getString("standardContext.filterMap.name", filterName));

		if (!filterMap.getMatchAllServletNames() &&
				!filterMap.getMatchAllUrlPatterns() &&
				(servletNames.length == 0) && (urlPatterns.length == 0))
			throw new IllegalArgumentException
					(sm.getString("standardContext.filterMap.either"));
		// FIXME: Older spec revisions may still check this
        /*
        if ((servletNames.length != 0) && (urlPatterns.length != 0))
            throw new IllegalArgumentException
                (sm.getString("standardContext.filterMap.either"));
        */
		for (int i = 0; i < urlPatterns.length; i++) {
			if (!validateURLPattern(urlPatterns[i])) {
				throw new IllegalArgumentException
						(sm.getString("standardContext.filterMap.pattern",
								urlPatterns[i]));
			}
		}
	}

	/**
	 * Add the classname of an InstanceListener to be added to each
	 * Wrapper appended to this Context.
	 *
	 * @param listener Java class name of an InstanceListener class
	 */
	@Override
	public void addInstanceListener(String listener) {

		synchronized (instanceListenersLock) {
			String results[] = new String[instanceListeners.length + 1];
			for (int i = 0; i < instanceListeners.length; i++)
				results[i] = instanceListeners[i];
			results[instanceListeners.length] = listener;
			instanceListeners = results;
		}
		fireContainerEvent("addInstanceListener", listener);

	}

	/**
	 * Add a Locale Encoding Mapping (see Sec 5.4 of Servlet spec 2.4)
	 *
	 * @param locale   locale to map an encoding for
	 * @param encoding encoding to be used for a give locale
	 */
	@Override
	public void addLocaleEncodingMappingParameter(String locale, String encoding) {
		getCharsetMapper().addCharsetMappingFromDeploymentDescriptor(locale, encoding);
	}

	/**
	 * Add a message destination for this web application.
	 *
	 * @param md New message destination
	 */
	public void addMessageDestination(MessageDestination md) {

		synchronized (messageDestinations) {
			messageDestinations.put(md.getName(), md);
		}
		fireContainerEvent("addMessageDestination", md.getName());

	}

	/**
	 * Add a message destination reference for this web application.
	 *
	 * @param mdr New message destination reference
	 */
	public void addMessageDestinationRef
	(MessageDestinationRef mdr) {

		namingResources.addMessageDestinationRef(mdr);
		fireContainerEvent("addMessageDestinationRef", mdr.getName());

	}

	/**
	 * Add a new MIME mapping, replacing any existing mapping for
	 * the specified extension.
	 *
	 * @param extension Filename extension being mapped
	 * @param mimeType  Corresponding MIME type
	 */
	@Override
	public void addMimeMapping(String extension, String mimeType) {

		synchronized (mimeMappings) {
			mimeMappings.put(extension, mimeType);
		}
		fireContainerEvent("addMimeMapping", extension);

	}

	/**
	 * Add a new context initialization parameter.
	 *
	 * @param name  Name of the new parameter
	 * @param value Value of the new  parameter
	 * @throws IllegalArgumentException if the name or value is missing,
	 *                                  or if this context initialization parameter has already been
	 *                                  registered
	 */
	@Override
	public void addParameter(String name, String value) {
		// Validate the proposed context initialization parameter
		if ((name == null) || (value == null))
			throw new IllegalArgumentException
					(sm.getString("standardContext.parameter.required"));
		if (parameters.get(name) != null)
			throw new IllegalArgumentException
					(sm.getString("standardContext.parameter.duplicate", name));

		// Add this parameter to our defined set
		synchronized (parameters) {
			parameters.put(name, value);
		}
		fireContainerEvent("addParameter", name);

	}

	/**
	 * Add a security role reference for this web application.
	 *
	 * @param role Security role used in the application
	 * @param link Actual security role to check for
	 */
	@Override
	public void addRoleMapping(String role, String link) {

		synchronized (roleMappings) {
			roleMappings.put(role, link);
		}
		fireContainerEvent("addRoleMapping", role);

	}

	/**
	 * Add a new security role for this web application.
	 *
	 * @param role New security role
	 */
	@Override
	public void addSecurityRole(String role) {

		synchronized (securityRolesLock) {
			String results[] = new String[securityRoles.length + 1];
			for (int i = 0; i < securityRoles.length; i++)
				results[i] = securityRoles[i];
			results[securityRoles.length] = role;
			securityRoles = results;
		}
		fireContainerEvent("addSecurityRole", role);

	}

	/**
	 * Add a new servlet mapping, replacing any existing mapping for
	 * the specified pattern.
	 *
	 * @param pattern URL pattern to be mapped
	 * @param name    Name of the corresponding servlet to execute
	 * @throws IllegalArgumentException if the specified servlet name
	 *                                  is not known to this Context
	 */
	@Override
	public void addServletMapping(String pattern, String name) {
		addServletMapping(pattern, name, false);
	}

	/**
	 * Add a new servlet mapping, replacing any existing mapping for
	 * the specified pattern.
	 *
	 * @param pattern     URL pattern to be mapped
	 * @param name        Name of the corresponding servlet to execute
	 * @param jspWildCard true if name identifies the JspServlet
	 *                    and pattern contains a wildcard; false otherwise
	 * @throws IllegalArgumentException if the specified servlet name
	 *                                  is not known to this Context
	 */
	@Override
	public void addServletMapping(String pattern, String name,
	                              boolean jspWildCard) {
		// Validate the proposed mapping
		if (findChild(name) == null)
			throw new IllegalArgumentException
					(sm.getString("standardContext.servletMap.name", name));
		String decodedPattern = adjustURLPattern(RequestUtil.URLDecode(pattern));
		if (!validateURLPattern(decodedPattern))
			throw new IllegalArgumentException
					(sm.getString("standardContext.servletMap.pattern", decodedPattern));

		// Add this mapping to our registered set
		synchronized (servletMappingsLock) {
			String name2 = servletMappings.get(decodedPattern);
			if (name2 != null) {
				// Don't allow more than one servlet on the same pattern
				Wrapper wrapper = (Wrapper) findChild(name2);
				wrapper.removeMapping(decodedPattern);
				mapper.removeWrapper(decodedPattern);
			}
			servletMappings.put(decodedPattern, name);
		}
		Wrapper wrapper = (Wrapper) findChild(name);
		wrapper.addMapping(decodedPattern);

		// Update context mapper
		mapper.addWrapper(decodedPattern, wrapper, jspWildCard,
				resourceOnlyServlets.contains(name));

		fireContainerEvent("addServletMapping", decodedPattern);

	}

	/**
	 * Add a new watched resource to the set recognized by this Context.
	 *
	 * @param name New watched resource file name
	 */
	@Override
	public void addWatchedResource(String name) {

		synchronized (watchedResourcesLock) {
			String results[] = new String[watchedResources.length + 1];
			for (int i = 0; i < watchedResources.length; i++)
				results[i] = watchedResources[i];
			results[watchedResources.length] = name;
			watchedResources = results;
		}
		fireContainerEvent("addWatchedResource", name);

	}

	/**
	 * Add a new welcome file to the set recognized by this Context.
	 *
	 * @param name New welcome file name
	 */
	@Override
	public void addWelcomeFile(String name) {

		synchronized (welcomeFilesLock) {
			// Welcome files from the application deployment descriptor
			// completely replace those from the default conf/web.xml file
			if (replaceWelcomeFiles) {
				fireContainerEvent(CLEAR_WELCOME_FILES_EVENT, null);
				welcomeFiles = new String[0];
				setReplaceWelcomeFiles(false);
			}
			String results[] = new String[welcomeFiles.length + 1];
			for (int i = 0; i < welcomeFiles.length; i++)
				results[i] = welcomeFiles[i];
			results[welcomeFiles.length] = name;
			welcomeFiles = results;
		}
		if (this.getState().equals(LifecycleState.STARTED))
			fireContainerEvent(ADD_WELCOME_FILE_EVENT, name);
	}

	/**
	 * Add the classname of a LifecycleListener to be added to each
	 * Wrapper appended to this Context.
	 *
	 * @param listener Java class name of a LifecycleListener class
	 */
	@Override
	public void addWrapperLifecycle(String listener) {

		synchronized (wrapperLifecyclesLock) {
			String results[] = new String[wrapperLifecycles.length + 1];
			for (int i = 0; i < wrapperLifecycles.length; i++)
				results[i] = wrapperLifecycles[i];
			results[wrapperLifecycles.length] = listener;
			wrapperLifecycles = results;
		}
		fireContainerEvent("addWrapperLifecycle", listener);

	}

	/**
	 * Add the classname of a ContainerListener to be added to each
	 * Wrapper appended to this Context.
	 *
	 * @param listener Java class name of a ContainerListener class
	 */
	@Override
	public void addWrapperListener(String listener) {

		synchronized (wrapperListenersLock) {
			String results[] = new String[wrapperListeners.length + 1];
			for (int i = 0; i < wrapperListeners.length; i++)
				results[i] = wrapperListeners[i];
			results[wrapperListeners.length] = listener;
			wrapperListeners = results;
		}
		fireContainerEvent("addWrapperListener", listener);

	}

	/**
	 * Factory method to create and return a new Wrapper instance, of
	 * the Java implementation class appropriate for this Context
	 * implementation.  The constructor of the instantiated Wrapper
	 * will have been called, but no properties will have been set.
	 */
	@Override
	public Wrapper createWrapper() {

		Wrapper wrapper = null;
		if (wrapperClass != null) {
			try {
				wrapper = (Wrapper) wrapperClass.newInstance();
			} catch (Throwable t) {
				ExceptionUtils.handleThrowable(t);
				log.error("createWrapper", t);
				return (null);
			}
		} else {
			wrapper = new StandardWrapper();
		}

		synchronized (instanceListenersLock) {
			for (int i = 0; i < instanceListeners.length; i++) {
				try {
					Class<?> clazz = Class.forName(instanceListeners[i]);
					InstanceListener listener =
							(InstanceListener) clazz.newInstance();
					wrapper.addInstanceListener(listener);
				} catch (Throwable t) {
					ExceptionUtils.handleThrowable(t);
					log.error("createWrapper", t);
					return (null);
				}
			}
		}

		synchronized (wrapperLifecyclesLock) {
			for (int i = 0; i < wrapperLifecycles.length; i++) {
				try {
					Class<?> clazz = Class.forName(wrapperLifecycles[i]);
					LifecycleListener listener =
							(LifecycleListener) clazz.newInstance();
					wrapper.addLifecycleListener(listener);
				} catch (Throwable t) {
					ExceptionUtils.handleThrowable(t);
					log.error("createWrapper", t);
					return (null);
				}
			}
		}

		synchronized (wrapperListenersLock) {
			for (int i = 0; i < wrapperListeners.length; i++) {
				try {
					Class<?> clazz = Class.forName(wrapperListeners[i]);
					ContainerListener listener =
							(ContainerListener) clazz.newInstance();
					wrapper.addContainerListener(listener);
				} catch (Throwable t) {
					ExceptionUtils.handleThrowable(t);
					log.error("createWrapper", t);
					return (null);
				}
			}
		}

		return (wrapper);

	}

	/**
	 * Return the set of application listener class names configured
	 * for this application.
	 */
	@Override
	public String[] findApplicationListeners() {

		ArrayList<String> list =
				new ArrayList<String>(applicationListeners.length);
		for (ApplicationListener applicationListener : applicationListeners) {
			list.add(applicationListener.getClassName());
		}

		return list.toArray(new String[list.size()]);

	}

	/**
	 * Return the set of application parameters for this application.
	 */
	@Override
	public ApplicationParameter[] findApplicationParameters() {

		synchronized (applicationParametersLock) {
			return (applicationParameters);
		}

	}

	/**
	 * Return the security constraints for this web application.
	 * If there are none, a zero-length array is returned.
	 */
	@Override
	public SecurityConstraint[] findConstraints() {

		return (constraints);

	}

	/**
	 * Return the error page entry for the specified HTTP error code,
	 * if any; otherwise return <code>null</code>.
	 *
	 * @param errorCode Error code to look up
	 */
	@Override
	public ErrorPage findErrorPage(int errorCode) {
		if (errorCode == 200) {
			return (okErrorPage);
		} else {
			return (statusPages.get(Integer.valueOf(errorCode)));
		}

	}

	/**
	 * Return the error page entry for the specified Java exception type,
	 * if any; otherwise return <code>null</code>.
	 *
	 * @param exceptionType Exception type to look up
	 */
	@Override
	public ErrorPage findErrorPage(String exceptionType) {

		synchronized (exceptionPages) {
			return (exceptionPages.get(exceptionType));
		}

	}

	/**
	 * Return the set of defined error pages for all specified error codes
	 * and exception types.
	 */
	@Override
	public ErrorPage[] findErrorPages() {

		synchronized (exceptionPages) {
			synchronized (statusPages) {
				ErrorPage results1[] = new ErrorPage[exceptionPages.size()];
				results1 = exceptionPages.values().toArray(results1);
				ErrorPage results2[] = new ErrorPage[statusPages.size()];
				results2 = statusPages.values().toArray(results2);
				ErrorPage results[] =
						new ErrorPage[results1.length + results2.length];
				for (int i = 0; i < results1.length; i++)
					results[i] = results1[i];
				for (int i = results1.length; i < results.length; i++)
					results[i] = results2[i - results1.length];
				return (results);
			}
		}

	}

	/**
	 * Return the filter definition for the specified filter name, if any;
	 * otherwise return <code>null</code>.
	 *
	 * @param filterName Filter name to look up
	 */
	@Override
	public FilterDef findFilterDef(String filterName) {

		synchronized (filterDefs) {
			return (filterDefs.get(filterName));
		}

	}

	/**
	 * Return the set of defined filters for this Context.
	 */
	@Override
	public FilterDef[] findFilterDefs() {

		synchronized (filterDefs) {
			FilterDef results[] = new FilterDef[filterDefs.size()];
			return (filterDefs.values().toArray(results));
		}

	}

	/**
	 * Return the set of filter mappings for this Context.
	 */
	@Override
	public FilterMap[] findFilterMaps() {
		return filterMaps.asArray();
	}

	/**
	 * Return the set of InstanceListener classes that will be added to
	 * newly created Wrappers automatically.
	 */
	@Override
	public String[] findInstanceListeners() {

		synchronized (instanceListenersLock) {
			return (instanceListeners);
		}

	}

	/**
	 * FIXME: Fooling introspection ...
	 */
	@Deprecated
	public Context findMappingObject() {
		return (Context) getMappingObject();
	}

	/**
	 * Return the message destination with the specified name, if any;
	 * otherwise, return <code>null</code>.
	 *
	 * @param name Name of the desired message destination
	 */
	public MessageDestination findMessageDestination(String name) {

		synchronized (messageDestinations) {
			return (messageDestinations.get(name));
		}

	}

	/**
	 * Return the set of defined message destinations for this web
	 * application.  If none have been defined, a zero-length array
	 * is returned.
	 */
	public MessageDestination[] findMessageDestinations() {

		synchronized (messageDestinations) {
			MessageDestination results[] =
					new MessageDestination[messageDestinations.size()];
			return (messageDestinations.values().toArray(results));
		}

	}

	/**
	 * Return the message destination ref with the specified name, if any;
	 * otherwise, return <code>null</code>.
	 *
	 * @param name Name of the desired message destination ref
	 */
	public MessageDestinationRef
	findMessageDestinationRef(String name) {

		return namingResources.findMessageDestinationRef(name);

	}

	/**
	 * Return the set of defined message destination refs for this web
	 * application.  If none have been defined, a zero-length array
	 * is returned.
	 */
	public MessageDestinationRef[]
	findMessageDestinationRefs() {

		return namingResources.findMessageDestinationRefs();

	}

	/**
	 * Return the MIME type to which the specified extension is mapped,
	 * if any; otherwise return <code>null</code>.
	 *
	 * @param extension Extension to map to a MIME type
	 */
	@Override
	public String findMimeMapping(String extension) {

		return (mimeMappings.get(extension));

	}

	/**
	 * Return the extensions for which MIME mappings are defined.  If there
	 * are none, a zero-length array is returned.
	 */
	@Override
	public String[] findMimeMappings() {

		synchronized (mimeMappings) {
			String results[] = new String[mimeMappings.size()];
			return
					(mimeMappings.keySet().toArray(results));
		}

	}

	/**
	 * Return the value for the specified context initialization
	 * parameter name, if any; otherwise return <code>null</code>.
	 *
	 * @param name Name of the parameter to return
	 */
	@Override
	public String findParameter(String name) {

		synchronized (parameters) {
			return (parameters.get(name));
		}

	}

	/**
	 * Return the names of all defined context initialization parameters
	 * for this Context.  If no parameters are defined, a zero-length
	 * array is returned.
	 */
	@Override
	public String[] findParameters() {

		synchronized (parameters) {
			String results[] = new String[parameters.size()];
			return (parameters.keySet().toArray(results));
		}

	}

	/**
	 * For the given security role (as used by an application), return the
	 * corresponding role name (as defined by the underlying Realm) if there
	 * is one.  Otherwise, return the specified role unchanged.
	 *
	 * @param role Security role to map
	 */
	@Override
	public String findRoleMapping(String role) {

		String realRole = null;
		synchronized (roleMappings) {
			realRole = roleMappings.get(role);
		}
		if (realRole != null)
			return (realRole);
		else
			return (role);

	}

	/**
	 * Return <code>true</code> if the specified security role is defined
	 * for this application; otherwise return <code>false</code>.
	 *
	 * @param role Security role to verify
	 */
	@Override
	public boolean findSecurityRole(String role) {

		synchronized (securityRolesLock) {
			for (int i = 0; i < securityRoles.length; i++) {
				if (role.equals(securityRoles[i]))
					return (true);
			}
		}
		return (false);

	}

	/**
	 * Return the security roles defined for this application.  If none
	 * have been defined, a zero-length array is returned.
	 */
	@Override
	public String[] findSecurityRoles() {

		synchronized (securityRolesLock) {
			return (securityRoles);
		}

	}

	/**
	 * Return the servlet name mapped by the specified pattern (if any);
	 * otherwise return <code>null</code>.
	 *
	 * @param pattern Pattern for which a mapping is requested
	 */
	@Override
	public String findServletMapping(String pattern) {

		synchronized (servletMappingsLock) {
			return (servletMappings.get(pattern));
		}

	}

	/**
	 * Return the patterns of all defined servlet mappings for this
	 * Context.  If no mappings are defined, a zero-length array is returned.
	 */
	@Override
	public String[] findServletMappings() {

		synchronized (servletMappingsLock) {
			String results[] = new String[servletMappings.size()];
			return
					(servletMappings.keySet().toArray(results));
		}

	}

	/**
	 * Return the context-relative URI of the error page for the specified
	 * HTTP status code, if any; otherwise return <code>null</code>.
	 *
	 * @param status HTTP status code to look up
	 */
	@Override
	public String findStatusPage(int status) {

		ErrorPage errorPage = statusPages.get(Integer.valueOf(status));
		if (errorPage != null) {
			return errorPage.getLocation();
		}
		return null;

	}

	/**
	 * Return the set of HTTP status codes for which error pages have
	 * been specified.  If none are specified, a zero-length array
	 * is returned.
	 */
	@Override
	public int[] findStatusPages() {

		synchronized (statusPages) {
			int results[] = new int[statusPages.size()];
			Iterator<Integer> elements = statusPages.keySet().iterator();
			int i = 0;
			while (elements.hasNext())
				results[i++] = elements.next().intValue();
			return (results);
		}

	}

	/**
	 * Return <code>true</code> if the specified welcome file is defined
	 * for this Context; otherwise return <code>false</code>.
	 *
	 * @param name Welcome file to verify
	 */
	@Override
	public boolean findWelcomeFile(String name) {

		synchronized (welcomeFilesLock) {
			for (int i = 0; i < welcomeFiles.length; i++) {
				if (name.equals(welcomeFiles[i]))
					return (true);
			}
		}
		return (false);

	}

	/**
	 * Return the set of watched resources for this Context. If none are
	 * defined, a zero length array will be returned.
	 */
	@Override
	public String[] findWatchedResources() {
		synchronized (watchedResourcesLock) {
			return watchedResources;
		}
	}

	/**
	 * Return the set of welcome files defined for this Context.  If none are
	 * defined, a zero-length array is returned.
	 */
	@Override
	public String[] findWelcomeFiles() {

		synchronized (welcomeFilesLock) {
			return (welcomeFiles);
		}

	}

	/**
	 * Return the set of LifecycleListener classes that will be added to
	 * newly created Wrappers automatically.
	 */
	@Override
	public String[] findWrapperLifecycles() {

		synchronized (wrapperLifecyclesLock) {
			return (wrapperLifecycles);
		}

	}

	/**
	 * Return the set of ContainerListener classes that will be added to
	 * newly created Wrappers automatically.
	 */
	@Override
	public String[] findWrapperListeners() {

		synchronized (wrapperListenersLock) {
			return (wrapperListeners);
		}

	}

	/**
	 * Reload this web application, if reloading is supported.
	 * <p>
	 * <b>IMPLEMENTATION NOTE</b>:  This method is designed to deal with
	 * reloads required by changes to classes in the underlying repositories
	 * of our class loader and changes to the web.xml file. It does not handle
	 * changes to any context.xml file. If the context.xml has changed, you
	 * should stop this Context and create (and start) a new Context instance
	 * instead. Note that there is additional code in
	 * <code>CoyoteAdapter#postParseRequest()</code> to handle mapping requests
	 * to paused Contexts.
	 *
	 * @throws IllegalStateException if the <code>reloadable</code>
	 *                               property is set to <code>false</code>.
	 */
	@Override
	public synchronized void reload() {

		// Validate our current component state
		if (!getState().isAvailable())
			throw new IllegalStateException
					(sm.getString("standardContext.notStarted", getName()));

		if (log.isInfoEnabled())
			log.info(sm.getString("standardContext.reloadingStarted",
					getName()));

		// Stop accepting requests temporarily.
		setPaused(true);

		try {
			stop();
		} catch (LifecycleException e) {
			log.error(
					sm.getString("standardContext.stoppingContext", getName()), e);
		}

		try {
			start();
		} catch (LifecycleException e) {
			log.error(
					sm.getString("standardContext.startingContext", getName()), e);
		}

		setPaused(false);

		if (log.isInfoEnabled())
			log.info(sm.getString("standardContext.reloadingCompleted",
					getName()));

	}

	/**
	 * Remove the specified application listener class from the set of
	 * listeners for this application.
	 *
	 * @param listener Java class name of the listener to be removed
	 */
	@Override
	public void removeApplicationListener(String listener) {

		synchronized (applicationListenersLock) {

			// Make sure this listener is currently present
			int n = -1;
			for (int i = 0; i < applicationListeners.length; i++) {
				if (applicationListeners[i].getClassName().equals(listener)) {
					n = i;
					break;
				}
			}
			if (n < 0)
				return;

			// Remove the specified listener
			int j = 0;
			ApplicationListener results[] =
					new ApplicationListener[applicationListeners.length - 1];
			for (int i = 0; i < applicationListeners.length; i++) {
				if (i != n)
					results[j++] = applicationListeners[i];
			}
			applicationListeners = results;

		}

		// Inform interested listeners
		fireContainerEvent("removeApplicationListener", listener);

		// FIXME - behavior if already started?

	}

	/**
	 * Remove the application parameter with the specified name from
	 * the set for this application.
	 *
	 * @param name Name of the application parameter to remove
	 */
	@Override
	public void removeApplicationParameter(String name) {

		synchronized (applicationParametersLock) {

			// Make sure this parameter is currently present
			int n = -1;
			for (int i = 0; i < applicationParameters.length; i++) {
				if (name.equals(applicationParameters[i].getName())) {
					n = i;
					break;
				}
			}
			if (n < 0)
				return;

			// Remove the specified parameter
			int j = 0;
			ApplicationParameter results[] =
					new ApplicationParameter[applicationParameters.length - 1];
			for (int i = 0; i < applicationParameters.length; i++) {
				if (i != n)
					results[j++] = applicationParameters[i];
			}
			applicationParameters = results;

		}

		// Inform interested listeners
		fireContainerEvent("removeApplicationParameter", name);

	}

	/**
	 * Add a child Container, only if the proposed child is an implementation
	 * of Wrapper.
	 *
	 * @param child Child container to be added
	 * @throws IllegalArgumentException if the proposed container is
	 *                                  not an implementation of Wrapper
	 */
	@Override
	public void removeChild(Container child) {

		if (!(child instanceof Wrapper)) {
			throw new IllegalArgumentException
					(sm.getString("standardContext.notWrapper"));
		}

		super.removeChild(child);

	}

	/**
	 * Remove the specified security constraint from this web application.
	 *
	 * @param constraint Constraint to be removed
	 */
	@Override
	public void removeConstraint(SecurityConstraint constraint) {

		synchronized (constraintsLock) {

			// Make sure this constraint is currently present
			int n = -1;
			for (int i = 0; i < constraints.length; i++) {
				if (constraints[i].equals(constraint)) {
					n = i;
					break;
				}
			}
			if (n < 0)
				return;

			// Remove the specified constraint
			int j = 0;
			SecurityConstraint results[] =
					new SecurityConstraint[constraints.length - 1];
			for (int i = 0; i < constraints.length; i++) {
				if (i != n)
					results[j++] = constraints[i];
			}
			constraints = results;

		}

		// Inform interested listeners
		fireContainerEvent("removeConstraint", constraint);

	}

	/**
	 * Remove the error page for the specified error code or
	 * Java language exception, if it exists; otherwise, no action is taken.
	 *
	 * @param errorPage The error page definition to be removed
	 */
	@Override
	public void removeErrorPage(ErrorPage errorPage) {

		String exceptionType = errorPage.getExceptionType();
		if (exceptionType != null) {
			synchronized (exceptionPages) {
				exceptionPages.remove(exceptionType);
			}
		} else {
			synchronized (statusPages) {
				if (errorPage.getErrorCode() == 200) {
					this.okErrorPage = null;
				}
				statusPages.remove(Integer.valueOf(errorPage.getErrorCode()));
			}
		}
		fireContainerEvent("removeErrorPage", errorPage);

	}

	/**
	 * Remove the specified filter definition from this Context, if it exists;
	 * otherwise, no action is taken.
	 *
	 * @param filterDef Filter definition to be removed
	 */
	@Override
	public void removeFilterDef(FilterDef filterDef) {

		synchronized (filterDefs) {
			filterDefs.remove(filterDef.getFilterName());
		}
		fireContainerEvent("removeFilterDef", filterDef);

	}

	/**
	 * Remove a filter mapping from this Context.
	 *
	 * @param filterMap The filter mapping to be removed
	 */
	@Override
	public void removeFilterMap(FilterMap filterMap) {
		filterMaps.remove(filterMap);
		// Inform interested listeners
		fireContainerEvent("removeFilterMap", filterMap);
	}

	/**
	 * Remove a class name from the set of InstanceListener classes that
	 * will be added to newly created Wrappers.
	 *
	 * @param listener Class name of an InstanceListener class to be removed
	 */
	@Override
	public void removeInstanceListener(String listener) {

		synchronized (instanceListenersLock) {

			// Make sure this listener is currently present
			int n = -1;
			for (int i = 0; i < instanceListeners.length; i++) {
				if (instanceListeners[i].equals(listener)) {
					n = i;
					break;
				}
			}
			if (n < 0)
				return;

			// Remove the specified listener
			int j = 0;
			String results[] = new String[instanceListeners.length - 1];
			for (int i = 0; i < instanceListeners.length; i++) {
				if (i != n)
					results[j++] = instanceListeners[i];
			}
			instanceListeners = results;

		}

		// Inform interested listeners
		fireContainerEvent("removeInstanceListener", listener);

	}

	/**
	 * Remove any message destination with the specified name.
	 *
	 * @param name Name of the message destination to remove
	 */
	public void removeMessageDestination(String name) {

		synchronized (messageDestinations) {
			messageDestinations.remove(name);
		}
		fireContainerEvent("removeMessageDestination", name);

	}

	/**
	 * Remove any message destination ref with the specified name.
	 *
	 * @param name Name of the message destination ref to remove
	 */
	public void removeMessageDestinationRef(String name) {

		namingResources.removeMessageDestinationRef(name);
		fireContainerEvent("removeMessageDestinationRef", name);

	}

	/**
	 * Remove the MIME mapping for the specified extension, if it exists;
	 * otherwise, no action is taken.
	 *
	 * @param extension Extension to remove the mapping for
	 */
	@Override
	public void removeMimeMapping(String extension) {

		synchronized (mimeMappings) {
			mimeMappings.remove(extension);
		}
		fireContainerEvent("removeMimeMapping", extension);

	}

	/**
	 * Remove the context initialization parameter with the specified
	 * name, if it exists; otherwise, no action is taken.
	 *
	 * @param name Name of the parameter to remove
	 */
	@Override
	public void removeParameter(String name) {

		synchronized (parameters) {
			parameters.remove(name);
		}
		fireContainerEvent("removeParameter", name);

	}

	/**
	 * Remove any security role reference for the specified name
	 *
	 * @param role Security role (as used in the application) to remove
	 */
	@Override
	public void removeRoleMapping(String role) {

		synchronized (roleMappings) {
			roleMappings.remove(role);
		}
		fireContainerEvent("removeRoleMapping", role);

	}

	/**
	 * Remove any security role with the specified name.
	 *
	 * @param role Security role to remove
	 */
	@Override
	public void removeSecurityRole(String role) {

		synchronized (securityRolesLock) {

			// Make sure this security role is currently present
			int n = -1;
			for (int i = 0; i < securityRoles.length; i++) {
				if (role.equals(securityRoles[i])) {
					n = i;
					break;
				}
			}
			if (n < 0)
				return;

			// Remove the specified security role
			int j = 0;
			String results[] = new String[securityRoles.length - 1];
			for (int i = 0; i < securityRoles.length; i++) {
				if (i != n)
					results[j++] = securityRoles[i];
			}
			securityRoles = results;

		}

		// Inform interested listeners
		fireContainerEvent("removeSecurityRole", role);

	}

	/**
	 * Remove any servlet mapping for the specified pattern, if it exists;
	 * otherwise, no action is taken.
	 *
	 * @param pattern URL pattern of the mapping to remove
	 */
	@Override
	public void removeServletMapping(String pattern) {

		String name = null;
		synchronized (servletMappingsLock) {
			name = servletMappings.remove(pattern);
		}
		Wrapper wrapper = (Wrapper) findChild(name);
		if (wrapper != null) {
			wrapper.removeMapping(pattern);
		}
		mapper.removeWrapper(pattern);
		fireContainerEvent("removeServletMapping", pattern);

	}

	/**
	 * Remove the specified watched resource name from the list associated
	 * with this Context.
	 *
	 * @param name Name of the watched resource to be removed
	 */
	@Override
	public void removeWatchedResource(String name) {

		synchronized (watchedResourcesLock) {

			// Make sure this watched resource is currently present
			int n = -1;
			for (int i = 0; i < watchedResources.length; i++) {
				if (watchedResources[i].equals(name)) {
					n = i;
					break;
				}
			}
			if (n < 0)
				return;

			// Remove the specified watched resource
			int j = 0;
			String results[] = new String[watchedResources.length - 1];
			for (int i = 0; i < watchedResources.length; i++) {
				if (i != n)
					results[j++] = watchedResources[i];
			}
			watchedResources = results;

		}

		fireContainerEvent("removeWatchedResource", name);

	}

	/**
	 * Remove the specified welcome file name from the list recognized
	 * by this Context.
	 *
	 * @param name Name of the welcome file to be removed
	 */
	@Override
	public void removeWelcomeFile(String name) {

		synchronized (welcomeFilesLock) {

			// Make sure this welcome file is currently present
			int n = -1;
			for (int i = 0; i < welcomeFiles.length; i++) {
				if (welcomeFiles[i].equals(name)) {
					n = i;
					break;
				}
			}
			if (n < 0)
				return;

			// Remove the specified welcome file
			int j = 0;
			String results[] = new String[welcomeFiles.length - 1];
			for (int i = 0; i < welcomeFiles.length; i++) {
				if (i != n)
					results[j++] = welcomeFiles[i];
			}
			welcomeFiles = results;

		}

		// Inform interested listeners
		if (this.getState().equals(LifecycleState.STARTED))
			fireContainerEvent(REMOVE_WELCOME_FILE_EVENT, name);

	}

	/**
	 * Remove a class name from the set of LifecycleListener classes that
	 * will be added to newly created Wrappers.
	 *
	 * @param listener Class name of a LifecycleListener class to be removed
	 */
	@Override
	public void removeWrapperLifecycle(String listener) {


		synchronized (wrapperLifecyclesLock) {

			// Make sure this lifecycle listener is currently present
			int n = -1;
			for (int i = 0; i < wrapperLifecycles.length; i++) {
				if (wrapperLifecycles[i].equals(listener)) {
					n = i;
					break;
				}
			}
			if (n < 0)
				return;

			// Remove the specified lifecycle listener
			int j = 0;
			String results[] = new String[wrapperLifecycles.length - 1];
			for (int i = 0; i < wrapperLifecycles.length; i++) {
				if (i != n)
					results[j++] = wrapperLifecycles[i];
			}
			wrapperLifecycles = results;

		}

		// Inform interested listeners
		fireContainerEvent("removeWrapperLifecycle", listener);

	}

	/**
	 * Remove a class name from the set of ContainerListener classes that
	 * will be added to newly created Wrappers.
	 *
	 * @param listener Class name of a ContainerListener class to be removed
	 */
	@Override
	public void removeWrapperListener(String listener) {


		synchronized (wrapperListenersLock) {

			// Make sure this listener is currently present
			int n = -1;
			for (int i = 0; i < wrapperListeners.length; i++) {
				if (wrapperListeners[i].equals(listener)) {
					n = i;
					break;
				}
			}
			if (n < 0)
				return;

			// Remove the specified listener
			int j = 0;
			String results[] = new String[wrapperListeners.length - 1];
			for (int i = 0; i < wrapperListeners.length; i++) {
				if (i != n)
					results[j++] = wrapperListeners[i];
			}
			wrapperListeners = results;

		}

		// Inform interested listeners
		fireContainerEvent("removeWrapperListener", listener);

	}

	/**
	 * Gets the cumulative processing times of all servlets in this
	 * StandardContext.
	 *
	 * @return Cumulative processing times of all servlets in this
	 * StandardContext
	 */
	public long getProcessingTime() {

		long result = 0;

		Container[] children = findChildren();
		if (children != null) {
			for (int i = 0; i < children.length; i++) {
				result += ((StandardWrapper) children[i]).getProcessingTime();
			}
		}

		return result;
	}

	/**
	 * Gets the maximum processing time of all servlets in this
	 * StandardContext.
	 *
	 * @return Maximum processing time of all servlets in this
	 * StandardContext
	 */
	public long getMaxTime() {

		long result = 0;
		long time;

		Container[] children = findChildren();
		if (children != null) {
			for (int i = 0; i < children.length; i++) {
				time = ((StandardWrapper) children[i]).getMaxTime();
				if (time > result)
					result = time;
			}
		}

		return result;
	}

	/**
	 * Gets the minimum processing time of all servlets in this
	 * StandardContext.
	 *
	 * @return Minimum processing time of all servlets in this
	 * StandardContext
	 */
	public long getMinTime() {

		long result = -1;
		long time;

		Container[] children = findChildren();
		if (children != null) {
			for (int i = 0; i < children.length; i++) {
				time = ((StandardWrapper) children[i]).getMinTime();
				if (result < 0 || time < result)
					result = time;
			}
		}

		return result;
	}

	/**
	 * Gets the cumulative request count of all servlets in this
	 * StandardContext.
	 *
	 * @return Cumulative request count of all servlets in this
	 * StandardContext
	 */
	public int getRequestCount() {

		int result = 0;

		Container[] children = findChildren();
		if (children != null) {
			for (int i = 0; i < children.length; i++) {
				result += ((StandardWrapper) children[i]).getRequestCount();
			}
		}

		return result;
	}

	/**
	 * Gets the cumulative error count of all servlets in this
	 * StandardContext.
	 *
	 * @return Cumulative error count of all servlets in this
	 * StandardContext
	 */
	public int getErrorCount() {

		int result = 0;

		Container[] children = findChildren();
		if (children != null) {
			for (int i = 0; i < children.length; i++) {
				result += ((StandardWrapper) children[i]).getErrorCount();
			}
		}

		return result;
	}

	/**
	 * Return the real path for a given virtual path, if possible; otherwise
	 * return <code>null</code>.
	 *
	 * @param path The path to the desired resource
	 */
	@Override
	public String getRealPath(String path) {
		if (webappResources instanceof BaseDirContext) {
			return ((BaseDirContext) webappResources).getRealPath(path);
		}
		return null;
	}

	// --------------------------------------------------------- Public Methods

	/**
	 * hook to register that we need to scan for security annotations.
	 *
	 * @param wrapper The wrapper for the Servlet that was added
	 */
	public ServletRegistration.Dynamic dynamicServletAdded(Wrapper wrapper) {
		Servlet s = wrapper.getServlet();
		if (s != null && createdServlets.contains(s)) {
			// Mark the wrapper to indicate annotations need to be scanned
			wrapper.setServletSecurityAnnotationScanRequired(true);
		}
		return new ApplicationServletRegistration(wrapper, this);
	}

	/**
	 * hook to track which registrations need annotation scanning
	 *
	 * @param servlet
	 */
	public void dynamicServletCreated(Servlet servlet) {
		createdServlets.add(servlet);
	}

	/**
	 * Configure and initialize the set of filters for this Context.
	 * Return <code>true</code> if all filter initialization completed
	 * successfully, or <code>false</code> otherwise.
	 */
	public boolean filterStart() {

		if (getLogger().isDebugEnabled())
			getLogger().debug("Starting filters");
		// Instantiate and record a FilterConfig for each defined filter
		boolean ok = true;
		synchronized (filterConfigs) {
			filterConfigs.clear();
			for (Entry<String, FilterDef> entry : filterDefs.entrySet()) {
				String name = entry.getKey();
				if (getLogger().isDebugEnabled())
					getLogger().debug(" Starting filter '" + name + "'");
				ApplicationFilterConfig filterConfig = null;
				try {
					filterConfig =
							new ApplicationFilterConfig(this, entry.getValue());
					filterConfigs.put(name, filterConfig);
				} catch (Throwable t) {
					t = ExceptionUtils.unwrapInvocationTargetException(t);
					ExceptionUtils.handleThrowable(t);
					getLogger().error
							(sm.getString("standardContext.filterStart", name), t);
					ok = false;
				}
			}
		}

		return (ok);

	}

	/**
	 * Finalize and release the set of filters for this Context.
	 * Return <code>true</code> if all filter finalization completed
	 * successfully, or <code>false</code> otherwise.
	 */
	public boolean filterStop() {

		if (getLogger().isDebugEnabled())
			getLogger().debug("Stopping filters");

		// Release all Filter and FilterConfig instances
		synchronized (filterConfigs) {
			for (Entry<String, ApplicationFilterConfig> entry : filterConfigs.entrySet()) {
				if (getLogger().isDebugEnabled())
					getLogger().debug(" Stopping filter '" + entry.getKey() + "'");
				ApplicationFilterConfig filterConfig = entry.getValue();
				filterConfig.release();
			}
			filterConfigs.clear();
		}
		return (true);

	}

	/**
	 * Find and return the initialized <code>FilterConfig</code> for the
	 * specified filter name, if any; otherwise return <code>null</code>.
	 *
	 * @param name Name of the desired filter
	 */
	public FilterConfig findFilterConfig(String name) {

		return (filterConfigs.get(name));

	}

	/**
	 * Configure the set of instantiated application event listeners
	 * for this Context.  Return <code>true</code> if all listeners wre
	 * initialized successfully, or <code>false</code> otherwise.
	 */
	public boolean listenerStart() {

		if (log.isDebugEnabled())
			log.debug("Configuring application event listeners");

		// Instantiate the required listeners
		ApplicationListener listeners[] = applicationListeners;
		Object results[] = new Object[listeners.length];
		boolean ok = true;
		for (int i = 0; i < results.length; i++) {
			if (getLogger().isDebugEnabled())
				getLogger().debug(" Configuring event listener class '" +
						listeners[i] + "'");
			try {
				ApplicationListener listener = listeners[i];
				results[i] = getInstanceManager().newInstance(
						listener.getClassName());
				if (listener.isPluggabilityBlocked()) {
					noPluggabilityListeners.add(results[i]);
				}
			} catch (Throwable t) {
				t = ExceptionUtils.unwrapInvocationTargetException(t);
				ExceptionUtils.handleThrowable(t);
				getLogger().error
						(sm.getString("standardContext.applicationListener",
								listeners[i].getClassName()), t);
				ok = false;
			}
		}
		if (!ok) {
			getLogger().error(sm.getString("standardContext.applicationSkipped"));
			return (false);
		}

		// Sort listeners in two arrays
		ArrayList<Object> eventListeners = new ArrayList<Object>();
		ArrayList<Object> lifecycleListeners = new ArrayList<Object>();
		for (int i = 0; i < results.length; i++) {
			if ((results[i] instanceof ServletContextAttributeListener)
					|| (results[i] instanceof ServletRequestAttributeListener)
					|| (results[i] instanceof ServletRequestListener)
					|| (results[i] instanceof HttpSessionAttributeListener)) {
				eventListeners.add(results[i]);
			}
			if ((results[i] instanceof ServletContextListener)
					|| (results[i] instanceof HttpSessionListener)) {
				lifecycleListeners.add(results[i]);
			}
		}

		// Listener instances may have been added directly to this Context by
		// ServletContextInitializers and other code via the pluggability APIs.
		// Put them these listeners after the ones defined in web.xml and/or
		// annotations then overwrite the list of instances with the new, full
		// list.
		for (Object eventListener : getApplicationEventListeners()) {
			eventListeners.add(eventListener);
		}
		setApplicationEventListeners(eventListeners.toArray());
		for (Object lifecycleListener : getApplicationLifecycleListeners()) {
			lifecycleListeners.add(lifecycleListener);
			if (lifecycleListener instanceof ServletContextListener) {
				noPluggabilityListeners.add(lifecycleListener);
			}
		}
		setApplicationLifecycleListeners(lifecycleListeners.toArray());

		// Send application start events

		if (getLogger().isDebugEnabled())
			getLogger().debug("Sending application start events");

		// Ensure context is not null
		getServletContext();
		context.setNewServletContextListenerAllowed(false);

		Object instances[] = getApplicationLifecycleListeners();
		if (instances == null || instances.length == 0) {
			return ok;
		}

		ServletContextEvent event = new ServletContextEvent(getServletContext());
		ServletContextEvent tldEvent = null;
		if (noPluggabilityListeners.size() > 0) {
			noPluggabilityServletContext = new NoPluggabilityServletContext(getServletContext());
			tldEvent = new ServletContextEvent(noPluggabilityServletContext);
		}
		for (int i = 0; i < instances.length; i++) {
			if (instances[i] == null)
				continue;
			if (!(instances[i] instanceof ServletContextListener))
				continue;
			ServletContextListener listener =
					(ServletContextListener) instances[i];
			try {
				fireContainerEvent("beforeContextInitialized", listener);
				if (noPluggabilityListeners.contains(listener)) {
					listener.contextInitialized(tldEvent);
				} else {
					listener.contextInitialized(event);
				}
				fireContainerEvent("afterContextInitialized", listener);
			} catch (Throwable t) {
				ExceptionUtils.handleThrowable(t);
				fireContainerEvent("afterContextInitialized", listener);
				getLogger().error
						(sm.getString("standardContext.listenerStart",
								instances[i].getClass().getName()), t);
				ok = false;
			}
		}
		return (ok);

	}

	/**
	 * Send an application stop event to all interested listeners.
	 * Return <code>true</code> if all events were sent successfully,
	 * or <code>false</code> otherwise.
	 */
	public boolean listenerStop() {

		if (log.isDebugEnabled())
			log.debug("Sending application stop events");

		boolean ok = true;
		Object listeners[] = getApplicationLifecycleListeners();
		if (listeners != null) {
			ServletContextEvent event = new ServletContextEvent(getServletContext());
			ServletContextEvent tldEvent = null;
			if (noPluggabilityServletContext != null) {
				tldEvent = new ServletContextEvent(noPluggabilityServletContext);
			}
			for (int i = 0; i < listeners.length; i++) {
				int j = (listeners.length - 1) - i;
				if (listeners[j] == null)
					continue;
				if (listeners[j] instanceof ServletContextListener) {
					ServletContextListener listener =
							(ServletContextListener) listeners[j];
					try {
						fireContainerEvent("beforeContextDestroyed", listener);
						if (noPluggabilityListeners.contains(listener)) {
							listener.contextDestroyed(tldEvent);
						} else {
							listener.contextDestroyed(event);
						}
						fireContainerEvent("afterContextDestroyed", listener);
					} catch (Throwable t) {
						ExceptionUtils.handleThrowable(t);
						fireContainerEvent("afterContextDestroyed", listener);
						getLogger().error
								(sm.getString("standardContext.listenerStop",
										listeners[j].getClass().getName()), t);
						ok = false;
					}
				}
				try {
					if (getInstanceManager() != null) {
						getInstanceManager().destroyInstance(listeners[j]);
					}
				} catch (Throwable t) {
					t = ExceptionUtils.unwrapInvocationTargetException(t);
					ExceptionUtils.handleThrowable(t);
					getLogger().error
							(sm.getString("standardContext.listenerStop",
									listeners[j].getClass().getName()), t);
					ok = false;
				}
			}
		}

		// Annotation processing
		listeners = getApplicationEventListeners();
		if (listeners != null) {
			for (int i = 0; i < listeners.length; i++) {
				int j = (listeners.length - 1) - i;
				if (listeners[j] == null)
					continue;
				try {
					if (getInstanceManager() != null) {
						getInstanceManager().destroyInstance(listeners[j]);
					}
				} catch (Throwable t) {
					t = ExceptionUtils.unwrapInvocationTargetException(t);
					ExceptionUtils.handleThrowable(t);
					getLogger().error
							(sm.getString("standardContext.listenerStop",
									listeners[j].getClass().getName()), t);
					ok = false;
				}
			}
		}

		setApplicationEventListeners(null);
		setApplicationLifecycleListeners(null);

		noPluggabilityServletContext = null;
		noPluggabilityListeners.clear();

		return ok;
	}

	/**
	 * Allocate resources, including proxy.
	 * Return <code>true</code> if initialization was successfull,
	 * or <code>false</code> otherwise.
	 */
	public boolean resourcesStart() {

		boolean ok = true;

		Hashtable<String, String> env = new Hashtable<String, String>();
		if (getParent() != null)
			env.put(ProxyDirContext.HOST, getParent().getName());
		env.put(ProxyDirContext.CONTEXT, getName());

		try {
			ProxyDirContext proxyDirContext =
					new ProxyDirContext(env, webappResources);
			if (webappResources instanceof FileDirContext) {
				filesystemBased = true;
				((FileDirContext) webappResources).setAllowLinking
						(isAllowLinking());
			}
			if (webappResources instanceof BaseDirContext) {
				((BaseDirContext) webappResources).setDocBase(getBasePath());
				((BaseDirContext) webappResources).setCached
						(isCachingAllowed());
				((BaseDirContext) webappResources).setCacheTTL(getCacheTTL());
				((BaseDirContext) webappResources).setCacheMaxSize
						(getCacheMaxSize());
				((BaseDirContext) webappResources).allocate();
				// Alias support
				((BaseDirContext) webappResources).setAliases(getAliases());

				if (effectiveMajorVersion >= 3 && addWebinfClassesResources) {
					try {
						DirContext webInfCtx =
								(DirContext) webappResources.lookup(
										"/WEB-INF/classes");
						// Do the lookup to make sure it exists
						webInfCtx.lookup("META-INF/resources");
						((BaseDirContext) webappResources).addAltDirContext(
								webInfCtx);
					} catch (NamingException e) {
						// Doesn't exist - ignore and carry on
					}
				}
			}
			// Register the cache in JMX
			if (isCachingAllowed() && proxyDirContext.getCache() != null) {
				String contextName = getName();
				if (!contextName.startsWith("/")) {
					contextName = "/" + contextName;
				}
				ObjectName resourcesName =
						new ObjectName(this.getDomain() + ":type=Cache,host="
								+ getHostname() + ",context=" + contextName);
				Registry.getRegistry(null, null).registerComponent
						(proxyDirContext.getCache(), resourcesName, null);
			}
			this.resources = proxyDirContext;
		} catch (Throwable t) {
			ExceptionUtils.handleThrowable(t);
			log.error(sm.getString("standardContext.resourcesStart"), t);
			ok = false;
		}

		return (ok);

	}

	/**
	 * Deallocate resources and destroy proxy.
	 */
	public boolean resourcesStop() {

		boolean ok = true;

		try {
			if (resources != null) {
				if (resources instanceof Lifecycle) {
					((Lifecycle) resources).stop();
				}
				if (webappResources instanceof BaseDirContext) {
					((BaseDirContext) webappResources).release();
				}
				// Unregister the cache in JMX
				if (isCachingAllowed()) {
					String contextName = getName();
					if (!contextName.startsWith("/")) {
						contextName = "/" + contextName;
					}
					ObjectName resourcesName =
							new ObjectName(this.getDomain()
									+ ":type=Cache,host="
									+ getHostname() + ",context="
									+ contextName);
					Registry.getRegistry(null, null)
							.unregisterComponent(resourcesName);
				}
			}
		} catch (Throwable t) {
			ExceptionUtils.handleThrowable(t);
			log.error(sm.getString("standardContext.resourcesStop"), t);
			ok = false;
		}

		this.resources = null;

		return (ok);

	}

	/**
	 * Load and initialize all servlets marked "load on startup" in the
	 * web application deployment descriptor.
	 *
	 * @param children Array of wrappers for all currently defined
	 *                 servlets (including those not declared load on startup)
	 */
	public boolean loadOnStartup(Container children[]) {

		// Collect "load on startup" servlets that need to be initialized
		TreeMap<Integer, ArrayList<Wrapper>> map =
				new TreeMap<Integer, ArrayList<Wrapper>>();
		for (int i = 0; i < children.length; i++) {
			Wrapper wrapper = (Wrapper) children[i];
			int loadOnStartup = wrapper.getLoadOnStartup();
			if (loadOnStartup < 0)
				continue;
			Integer key = Integer.valueOf(loadOnStartup);
			ArrayList<Wrapper> list = map.get(key);
			if (list == null) {
				list = new ArrayList<Wrapper>();
				map.put(key, list);
			}
			list.add(wrapper);
		}

		// Load the collected "load on startup" servlets
		for (ArrayList<Wrapper> list : map.values()) {
			for (Wrapper wrapper : list) {
				try {
					wrapper.load();
				} catch (ServletException e) {
					getLogger().error(sm.getString("standardContext.loadOnStartup.loadException",
							getName(), wrapper.getName()), StandardWrapper.getRootCause(e));
					// NOTE: load errors (including a servlet that throws
					// UnavailableException from the init() method) are NOT
					// fatal to application startup
					// unless failCtxIfServletStartFails="true" is specified
					if (getComputedFailCtxIfServletStartFails()) {
						return false;
					}
				}
			}
		}
		return true;

	}

	/**
	 * Start this component and implement the requirements
	 * of {@link org.apache.catalina.util.LifecycleBase#startInternal()}.
	 *
	 * @throws LifecycleException if this component detects a fatal error
	 *                            that prevents this component from being used
	 */
	@Override
	protected synchronized void startInternal() throws LifecycleException {

		if (log.isDebugEnabled())
			log.debug("Starting " + getBaseName());

		// Send j2ee.state.starting notification
		if (this.getObjectName() != null) {
			Notification notification = new Notification("j2ee.state.starting",
					this.getObjectName(), sequenceNumber.getAndIncrement());
			broadcaster.sendNotification(notification);
		}

		setConfigured(false);
		boolean ok = true;

		// Currently this is effectively a NO-OP but needs to be called to
		// ensure the NamingResources follows the correct lifecycle
		if (namingResources != null) {
			namingResources.start();
		}

		// Add missing components as necessary
		if (webappResources == null) {   // (1) Required by Loader
			if (log.isDebugEnabled())
				log.debug("Configuring default Resources");
			try {
				String docBase = getDocBase();
				if (docBase == null) {
					setResources(new EmptyDirContext());
				} else if (docBase.endsWith(".war")
						&& !(new File(getBasePath())).isDirectory()) {
					setResources(new WARDirContext());
				} else {
					setResources(new FileDirContext());
				}
			} catch (IllegalArgumentException e) {
				log.error(sm.getString("standardContext.resourcesInit"), e);
				ok = false;
			}
		}
		if (ok) {
			if (!resourcesStart()) {
				throw new LifecycleException("Error in resourceStart()");
			}
		}

		if (getLoader() == null) {
			WebappLoader webappLoader = new WebappLoader(getParentClassLoader());
			webappLoader.setDelegate(getDelegate());
			setLoader(webappLoader);
		}

		// Initialize character set mapper
		getCharsetMapper();

		// Post work directory
		postWorkDirectory();

		// Validate required extensions
		boolean dependencyCheck = true;
		try {
			dependencyCheck = ExtensionValidator.validateApplication
					(getResources(), this);
		} catch (IOException ioe) {
			log.error(sm.getString("standardContext.extensionValidationError"), ioe);
			dependencyCheck = false;
		}

		if (!dependencyCheck) {
			// do not make application available if dependency check fails
			ok = false;
		}

		// Reading the "catalina.useNaming" environment variable
		String useNamingProperty = System.getProperty("catalina.useNaming");
		if ((useNamingProperty != null)
				&& (useNamingProperty.equals("false"))) {
			useNaming = false;
		}

		if (ok && isUseNaming()) {
			if (getNamingContextListener() == null) {
				NamingContextListener ncl = new NamingContextListener();
				ncl.setName(getNamingContextName());
				ncl.setExceptionOnFailedWrite(getJndiExceptionOnFailedWrite());
				addLifecycleListener(ncl);
				setNamingContextListener(ncl);
			}
		}

		// Standard container startup
		if (log.isDebugEnabled())
			log.debug("Processing standard container startup");


		// Binding thread
		ClassLoader oldCCL = bindThread();

		try {

			if (ok) {

				// Start our subordinate components, if any
				if ((loader != null) && (loader instanceof Lifecycle))
					((Lifecycle) loader).start();

				// since the loader just started, the webapp classloader is now
				// created.
				// By calling unbindThread and bindThread in a row, we setup the
				// current Thread CCL to be the webapp classloader
				unbindThread(oldCCL);
				oldCCL = bindThread();

				// Initialize logger again. Other components might have used it
				// too early, so it should be reset.
				logger = null;
				getLogger();

				if ((cluster != null) && (cluster instanceof Lifecycle))
					((Lifecycle) cluster).start();
				Realm realm = getRealmInternal();
				if ((realm != null) && (realm instanceof Lifecycle))
					((Lifecycle) realm).start();
				if ((resources != null) && (resources instanceof Lifecycle))
					((Lifecycle) resources).start();

				// Notify our interested LifecycleListeners
				fireLifecycleEvent(Lifecycle.CONFIGURE_START_EVENT, null);

				// Start our child containers, if not already started
				for (Container child : findChildren()) {
					if (!child.getState().isAvailable()) {
						child.start();
					}
				}

				// Start the Valves in our pipeline (including the basic),
				// if any
				if (pipeline instanceof Lifecycle) {
					((Lifecycle) pipeline).start();
				}

				// Acquire clustered manager
				Manager contextManager = null;
				if (manager == null) {
					if (log.isDebugEnabled()) {
						log.debug(sm.getString("standardContext.cluster.noManager",
								Boolean.valueOf((getCluster() != null)),
								Boolean.valueOf(distributable)));
					}
					if ((getCluster() != null) && distributable) {
						try {
							contextManager = getCluster().createManager(getName());
						} catch (Exception ex) {
							log.error("standardContext.clusterFail", ex);
							ok = false;
						}
					} else {
						contextManager = new StandardManager();
					}
				}

				// Configure default manager if none was specified
				if (contextManager != null) {
					if (log.isDebugEnabled()) {
						log.debug(sm.getString("standardContext.manager",
								contextManager.getClass().getName()));
					}
					setManager(contextManager);
				}

				if (manager != null && (getCluster() != null) && distributable) {
					//let the cluster know that there is a context that is distributable
					//and that it has its own manager
					getCluster().registerManager(manager);
				}
			}

		} finally {
			// Unbinding thread
			unbindThread(oldCCL);
		}

		if (!getConfigured()) {
			log.error(sm.getString("standardContext.configurationFail"));
			ok = false;
		}

		// We put the resources into the servlet context
		if (ok)
			getServletContext().setAttribute
					(Globals.RESOURCES_ATTR, getResources());

		// Initialize associated mapper
		mapper.setContext(getPath(), welcomeFiles, resources);

		// Binding thread
		oldCCL = bindThread();

		if (ok) {
			if (getInstanceManager() == null) {
				javax.naming.Context context = null;
				if (isUseNaming() && getNamingContextListener() != null) {
					context = getNamingContextListener().getEnvContext();
				}
				Map<String, Map<String, String>> injectionMap = buildInjectionMap(
						getIgnoreAnnotations() ? new NamingResources() : getNamingResources());
				setInstanceManager(new DefaultInstanceManager(context,
						injectionMap, this, this.getClass().getClassLoader()));
				getServletContext().setAttribute(
						InstanceManager.class.getName(), getInstanceManager());
			}
		}

		try {
			// Create context attributes that will be required
			if (ok) {
				getServletContext().setAttribute(
						JarScanner.class.getName(), getJarScanner());
			}

			// Set up the context init params
			mergeParameters();

			// Call ServletContainerInitializers
			for (Map.Entry<ServletContainerInitializer, Set<Class<?>>> entry :
					initializers.entrySet()) {
				try {
					entry.getKey().onStartup(entry.getValue(),
							getServletContext());
				} catch (ServletException e) {
					log.error(sm.getString("standardContext.sciFail"), e);
					ok = false;
					break;
				}
			}

			// Configure and call application event listeners
			if (ok) {
				if (!listenerStart()) {
					log.error(sm.getString("standardContext.listenerFail"));
					ok = false;
				}
			}

			try {
				// Start manager
				if ((manager != null) && (manager instanceof Lifecycle)) {
					((Lifecycle) getManager()).start();
				}
			} catch (Exception e) {
				log.error(sm.getString("standardContext.managerFail"), e);
				ok = false;
			}

			// Configure and call application filters
			if (ok) {
				if (!filterStart()) {
					log.error(sm.getString("standardContext.filterFail"));
					ok = false;
				}
			}

			// Load and initialize all "load on startup" servlets
			if (ok) {
				if (!loadOnStartup(findChildren())) {
					log.error(sm.getString("standardContext.servletFail"));
					ok = false;
				}
			}

			// Start ContainerBackgroundProcessor thread
			super.threadStart();
		} finally {
			// Unbinding thread
			unbindThread(oldCCL);
		}

		// Set available status depending upon startup success
		if (ok) {
			if (log.isDebugEnabled())
				log.debug("Starting completed");
		} else {
			log.error(sm.getString("standardContext.startFailed", getName()));
		}

		startTime = System.currentTimeMillis();

		// Send j2ee.state.running notification
		if (ok && (this.getObjectName() != null)) {
			Notification notification =
					new Notification("j2ee.state.running", this.getObjectName(),
							sequenceNumber.getAndIncrement());
			broadcaster.sendNotification(notification);
		}

		// Close all JARs right away to avoid always opening a peak number
		// of files on startup
		if (getLoader() instanceof WebappLoader) {
			((WebappLoader) getLoader()).closeJARs(true);
		}

		// Reinitializing if something went wrong
		if (!ok) {
			setState(LifecycleState.FAILED);
		} else {
			setState(LifecycleState.STARTING);
		}
	}

	private Map<String, Map<String, String>> buildInjectionMap(NamingResources namingResources) {
		Map<String, Map<String, String>> injectionMap = new HashMap<String, Map<String, String>>();
		for (Injectable resource : namingResources.findLocalEjbs()) {
			addInjectionTarget(resource, injectionMap);
		}
		for (Injectable resource : namingResources.findEjbs()) {
			addInjectionTarget(resource, injectionMap);
		}
		for (Injectable resource : namingResources.findEnvironments()) {
			addInjectionTarget(resource, injectionMap);
		}
		for (Injectable resource : namingResources.findMessageDestinationRefs()) {
			addInjectionTarget(resource, injectionMap);
		}
		for (Injectable resource : namingResources.findResourceEnvRefs()) {
			addInjectionTarget(resource, injectionMap);
		}
		for (Injectable resource : namingResources.findResources()) {
			addInjectionTarget(resource, injectionMap);
		}
		for (Injectable resource : namingResources.findServices()) {
			addInjectionTarget(resource, injectionMap);
		}
		return injectionMap;
	}

	private void addInjectionTarget(Injectable resource, Map<String, Map<String, String>> injectionMap) {
		List<InjectionTarget> injectionTargets = resource.getInjectionTargets();
		if (injectionTargets != null && injectionTargets.size() > 0) {
			String jndiName = resource.getName();
			for (InjectionTarget injectionTarget : injectionTargets) {
				String clazz = injectionTarget.getTargetClass();
				Map<String, String> injections = injectionMap.get(clazz);
				if (injections == null) {
					injections = new HashMap<String, String>();
					injectionMap.put(clazz, injections);
				}
				injections.put(injectionTarget.getTargetName(), jndiName);
			}
		}
	}

	/**
	 * Merge the context initialization parameters specified in the application
	 * deployment descriptor with the application parameters described in the
	 * server configuration, respecting the <code>override</code> property of
	 * the application parameters appropriately.
	 */
	private void mergeParameters() {
		Map<String, String> mergedParams = new HashMap<String, String>();

		String names[] = findParameters();
		for (int i = 0; i < names.length; i++) {
			mergedParams.put(names[i], findParameter(names[i]));
		}

		ApplicationParameter params[] = findApplicationParameters();
		for (int i = 0; i < params.length; i++) {
			if (params[i].getOverride()) {
				if (mergedParams.get(params[i].getName()) == null) {
					mergedParams.put(params[i].getName(),
							params[i].getValue());
				}
			} else {
				mergedParams.put(params[i].getName(), params[i].getValue());
			}
		}

		ServletContext sc = getServletContext();
		for (Map.Entry<String, String> entry : mergedParams.entrySet()) {
			sc.setInitParameter(entry.getKey(), entry.getValue());
		}

	}

	/**
	 * Stop this component and implement the requirements
	 * of {@link org.apache.catalina.util.LifecycleBase#stopInternal()}.
	 *
	 * @throws LifecycleException if this component detects a fatal error
	 *                            that prevents this component from being used
	 */
	@Override
	protected synchronized void stopInternal() throws LifecycleException {

		// Send j2ee.state.stopping notification
		if (this.getObjectName() != null) {
			Notification notification =
					new Notification("j2ee.state.stopping", this.getObjectName(),
							sequenceNumber.getAndIncrement());
			broadcaster.sendNotification(notification);
		}

		setState(LifecycleState.STOPPING);

		// Binding thread
		ClassLoader oldCCL = bindThread();

		try {

			// Stop our child containers, if any
			final Container[] children = findChildren();

			ClassLoader old = bindThread();
			try {
				// Stop ContainerBackgroundProcessor thread
				threadStop();

				for (int i = 0; i < children.length; i++) {
					children[i].stop();
				}

				// Stop our filters
				filterStop();

				if (manager != null && manager instanceof Lifecycle &&
						((Lifecycle) manager).getState().isAvailable()) {
					((Lifecycle) manager).stop();
				}

				// Stop our application listeners
				listenerStop();
			} finally {
				unbindThread(old);
			}

			// Finalize our character set mapper
			setCharsetMapper(null);

			// Normal container shutdown processing
			if (log.isDebugEnabled())
				log.debug("Processing standard container shutdown");

			// JNDI resources are unbound in CONFIGURE_STOP_EVENT so stop
			// naming resources before they are unbound since NamingResources
			// does a JNDI lookup to retrieve the resource. This needs to be
			// after the application has finished with the resource
			if (namingResources != null) {
				namingResources.stop();
			}

			fireLifecycleEvent(Lifecycle.CONFIGURE_STOP_EVENT, null);

			// Stop the Valves in our pipeline (including the basic), if any
			if (pipeline instanceof Lifecycle &&
					((Lifecycle) pipeline).getState().isAvailable()) {
				((Lifecycle) pipeline).stop();
			}

			// Clear all application-originated servlet context attributes
			if (context != null)
				context.clearAttributes();

			// Stop resources
			resourcesStop();

			Realm realm = getRealmInternal();
			if ((realm != null) && (realm instanceof Lifecycle)) {
				((Lifecycle) realm).stop();
			}
			if ((cluster != null) && (cluster instanceof Lifecycle)) {
				((Lifecycle) cluster).stop();
			}
			if ((loader != null) && (loader instanceof Lifecycle)) {
				((Lifecycle) loader).stop();
			}

		} finally {

			// Unbinding thread
			unbindThread(oldCCL);

		}

		// Send j2ee.state.stopped notification
		if (this.getObjectName() != null) {
			Notification notification =
					new Notification("j2ee.state.stopped", this.getObjectName(),
							sequenceNumber.getAndIncrement());
			broadcaster.sendNotification(notification);
		}

		// Reset application context
		context = null;

		// This object will no longer be visible or used.
		try {
			resetContext();
		} catch (Exception ex) {
			log.error("Error resetting context " + this + " " + ex, ex);
		}

		//reset the instance manager
		setInstanceManager(null);

		if (log.isDebugEnabled())
			log.debug("Stopping complete");

	}

	/**
	 * Destroy needs to clean up the context completely.
	 * <p>
	 * The problem is that undoing all the config in start() and restoring
	 * a 'fresh' state is impossible. After stop()/destroy()/init()/start()
	 * we should have the same state as if a fresh start was done - i.e
	 * read modified web.xml, etc. This can only be done by completely
	 * removing the context object and remapping a new one, or by cleaning
	 * up everything.
	 * <p>
	 * XXX Should this be done in stop() ?
	 */
	@Override
	protected void destroyInternal() throws LifecycleException {

		// If in state NEW when destroy is called, the object name will never
		// have been set so the notification can't be created
		if (getObjectName() != null) {
			// Send j2ee.object.deleted notification
			Notification notification =
					new Notification("j2ee.object.deleted", this.getObjectName(),
							sequenceNumber.getAndIncrement());
			broadcaster.sendNotification(notification);
		}

		if (namingResources != null) {
			namingResources.destroy();
		}

		synchronized (instanceListenersLock) {
			instanceListeners = new String[0];
		}

		super.destroyInternal();
	}

	@Override
	public void backgroundProcess() {

		InstanceManager instanceManager = getInstanceManager();
		if (instanceManager instanceof DefaultInstanceManager) {
			try {
				((DefaultInstanceManager) instanceManager).backgroundProcess();
			} catch (Exception e) {
				log.warn(sm.getString(
						"standardContext.backgroundProcess.instanceManager",
						resources), e);
			}
		}
		super.backgroundProcess();
	}


	// ------------------------------------------------------ Protected Methods

	private void resetContext() throws Exception {
		// Restore the original state ( pre reading web.xml in start )
		// If you extend this - override this method and make sure to clean up

		// Don't reset anything that is read from a <Context.../> element since
		// <Context .../> elements are read at initialisation will not be read
		// again for this object
		for (Container child : findChildren()) {
			removeChild(child);
		}
		startupTime = 0;
		startTime = 0;
		tldScanTime = 0;

		// Bugzilla 32867
		distributable = false;

		applicationListeners = new ApplicationListener[0];
		applicationEventListenersObjects = new Object[0];
		applicationLifecycleListenersObjects = new Object[0];
		jspConfigDescriptor = new ApplicationJspConfigDescriptor();

		initializers.clear();

		createdServlets.clear();

		postConstructMethods.clear();
		preDestroyMethods.clear();

		if (log.isDebugEnabled())
			log.debug("resetContext " + getObjectName());
	}

	/**
	 * Return a String representation of this component.
	 */
	@Override
	public String toString() {

		StringBuilder sb = new StringBuilder();
		if (getParent() != null) {
			sb.append(getParent().toString());
			sb.append(".");
		}
		sb.append("StandardContext[");
		sb.append(getName());
		sb.append("]");
		return (sb.toString());

	}

	/**
	 * Adjust the URL pattern to begin with a leading slash, if appropriate
	 * (i.e. we are running a servlet 2.2 application).  Otherwise, return
	 * the specified URL pattern unchanged.
	 *
	 * @param urlPattern The URL pattern to be adjusted (if needed)
	 *                   and returned
	 */
	protected String adjustURLPattern(String urlPattern) {

		if (urlPattern == null)
			return (urlPattern);
		if (urlPattern.startsWith("/") || urlPattern.startsWith("*."))
			return (urlPattern);
		if (!isServlet22())
			return (urlPattern);
		if (log.isDebugEnabled())
			log.debug(sm.getString("standardContext.urlPattern.patternWarning",
					urlPattern));
		return ("/" + urlPattern);

	}

	/**
	 * Are we processing a version 2.2 deployment descriptor?
	 */
	@Override
	public boolean isServlet22() {
		return XmlIdentifiers.WEB_22_PUBLIC.equals(publicId);
	}

	@Override
	public Set<String> addServletSecurity(
			ApplicationServletRegistration registration,
			ServletSecurityElement servletSecurityElement) {

		Set<String> conflicts = new HashSet<String>();

		Collection<String> urlPatterns = registration.getMappings();
		for (String urlPattern : urlPatterns) {
			boolean foundConflict = false;

			SecurityConstraint[] securityConstraints =
					findConstraints();
			for (SecurityConstraint securityConstraint : securityConstraints) {

				SecurityCollection[] collections =
						securityConstraint.findCollections();
				for (SecurityCollection collection : collections) {
					if (collection.findPattern(urlPattern)) {
						// First pattern found will indicate if there is a
						// conflict since for any given pattern all matching
						// constraints will be from either the descriptor or
						// not. It is not permitted to have a mixture
						if (collection.isFromDescriptor()) {
							// Skip this pattern
							foundConflict = true;
							conflicts.add(urlPattern);
						} else {
							// Need to overwrite constraint for this pattern
							// so remove every pattern found

							// TODO spec 13.4.2 appears to say only the
							// conflicting pattern is overwritten, not the
							// entire security constraint.
							removeConstraint(securityConstraint);
						}
					}
					if (foundConflict) {
						break;
					}
				}
				if (foundConflict) {
					break;
				}
			}
			// TODO spec 13.4.2 appears to say that non-conflicting patterns are
			// still used.
			// TODO you can't calculate the eventual security constraint now,
			// you have to wait until the context is started, since application
			// code can add url patterns after calling setSecurity.
			if (!foundConflict) {
				SecurityConstraint[] newSecurityConstraints =
						SecurityConstraint.createConstraints(
								servletSecurityElement,
								urlPattern);
				for (SecurityConstraint securityConstraint :
						newSecurityConstraints) {
					addConstraint(securityConstraint);
				}
			}
		}

		return conflicts;

	}

	/**
	 * Return a File object representing the base directory for the
	 * entire servlet container (i.e. the Engine container if present).
	 */
	protected File engineBase() {
		String base = System.getProperty(Globals.CATALINA_BASE_PROP);
		if (base == null) {
			StandardEngine eng = (StandardEngine) this.getParent().getParent();
			base = eng.getBaseDir();
		}
		return (new File(base));
	}

	/**
	 * Bind current thread, both for CL purposes and for JNDI ENC support
	 * during : startup, shutdown and realoading of the context.
	 *
	 * @return the previous context class loader
	 */
	protected ClassLoader bindThread() {

		ClassLoader oldContextClassLoader =
				Thread.currentThread().getContextClassLoader();

		if (getResources() == null)
			return oldContextClassLoader;

		if (getLoader() != null && getLoader().getClassLoader() != null) {
			Thread.currentThread().setContextClassLoader
					(getLoader().getClassLoader());
		}

		DirContextURLStreamHandler.bindThread(getResources());

		if (isUseNaming()) {
			try {
				ContextBindings.bindThread(this, this);
			} catch (NamingException e) {
				// Silent catch, as this is a normal case during the early
				// startup stages
			}
		}

		return oldContextClassLoader;

	}

	/**
	 * Unbind thread.
	 */
	protected void unbindThread(ClassLoader oldContextClassLoader) {

		if (isUseNaming()) {
			ContextBindings.unbindThread(this, this);
		}

		DirContextURLStreamHandler.unbindThread();

		Thread.currentThread().setContextClassLoader(oldContextClassLoader);
	}

	/**
	 * Get base path.
	 */
	protected String getBasePath() {
		String docBase = null;
		Container container = this;
		while (container != null) {
			if (container instanceof Host)
				break;
			container = container.getParent();
		}
		File file = new File(getDocBase());
		if (!file.isAbsolute()) {
			if (container == null) {
				docBase = (new File(engineBase(), getDocBase())).getPath();
			} else {
				// Use the "appBase" property of this container
				String appBase = ((Host) container).getAppBase();
				file = new File(appBase);
				if (!file.isAbsolute())
					file = new File(engineBase(), appBase);
				docBase = (new File(file, getDocBase())).getPath();
			}
		} else {
			docBase = file.getPath();
		}
		return docBase;
	}

	/**
	 * Get app base.
	 */
	protected String getAppBase() {
		String appBase = null;
		Container container = this;
		while (container != null) {
			if (container instanceof Host)
				break;
			container = container.getParent();
		}
		if (container != null) {
			appBase = ((Host) container).getAppBase();
		}
		return appBase;
	}

	/**
	 * Get naming context full name.
	 */
	private String getNamingContextName() {
		if (namingContextName == null) {
			Container parent = getParent();
			if (parent == null) {
				namingContextName = getName();
			} else {
				Stack<String> stk = new Stack<String>();
				StringBuilder buff = new StringBuilder();
				while (parent != null) {
					stk.push(parent.getName());
					parent = parent.getParent();
				}
				while (!stk.empty()) {
					buff.append("/" + stk.pop());
				}
				buff.append(getName());
				namingContextName = buff.toString();
			}
		}
		return namingContextName;
	}

	/**
	 * Naming context listener accessor.
	 */
	public NamingContextListener getNamingContextListener() {
		return namingContextListener;
	}

	/**
	 * Naming context listener setter.
	 */
	public void setNamingContextListener(NamingContextListener namingContextListener) {
		this.namingContextListener = namingContextListener;
	}

	/**
	 * Return the request processing paused flag for this Context.
	 */
	@Override
	public boolean getPaused() {

		return (this.paused);

	}

	/**
	 * Set the request processing paused flag for this Context.
	 *
	 * @param paused The new request processing paused flag
	 */
	private void setPaused(boolean paused) {

		this.paused = paused;

	}

	/**
	 * @deprecated Unused. Will be removed in Tomcat 8.0.x.
	 */
	@Deprecated
	public String getHostname() {
		Container parentHost = getParent();
		if (parentHost != null) {
			hostName = parentHost.getName();
		}
		if ((hostName == null) || (hostName.length() < 1))
			hostName = "_";
		return hostName;
	}

	@Override
	public boolean fireRequestInitEvent(ServletRequest request) {

		Object instances[] = getApplicationEventListeners();

		if ((instances != null) && (instances.length > 0)) {

			ServletRequestEvent event =
					new ServletRequestEvent(getServletContext(), request);

			for (int i = 0; i < instances.length; i++) {
				if (instances[i] == null)
					continue;
				if (!(instances[i] instanceof ServletRequestListener))
					continue;
				ServletRequestListener listener =
						(ServletRequestListener) instances[i];

				try {
					listener.requestInitialized(event);
				} catch (Throwable t) {
					ExceptionUtils.handleThrowable(t);
					getLogger().error(sm.getString(
							"standardContext.requestListener.requestInit",
							instances[i].getClass().getName()), t);
					request.setAttribute(RequestDispatcher.ERROR_EXCEPTION, t);
					return false;
				}
			}
		}
		return true;
	}

	@Override
	public boolean fireRequestDestroyEvent(ServletRequest request) {
		Object instances[] = getApplicationEventListeners();

		if ((instances != null) && (instances.length > 0)) {

			ServletRequestEvent event =
					new ServletRequestEvent(getServletContext(), request);

			for (int i = 0; i < instances.length; i++) {
				int j = (instances.length - 1) - i;
				if (instances[j] == null)
					continue;
				if (!(instances[j] instanceof ServletRequestListener))
					continue;
				ServletRequestListener listener =
						(ServletRequestListener) instances[j];

				try {
					listener.requestDestroyed(event);
				} catch (Throwable t) {
					ExceptionUtils.handleThrowable(t);
					getLogger().error(sm.getString(
							"standardContext.requestListener.requestInit",
							instances[j].getClass().getName()), t);
					request.setAttribute(RequestDispatcher.ERROR_EXCEPTION, t);
					return false;
				}
			}
		}
		return true;
	}

	@Override
	public void addPostConstructMethod(String clazz, String method) {
		if (clazz == null || method == null)
			throw new IllegalArgumentException(
					sm.getString("standardContext.postconstruct.required"));
		if (postConstructMethods.get(clazz) != null)
			throw new IllegalArgumentException(sm.getString(
					"standardContext.postconstruct.duplicate", clazz));

		postConstructMethods.put(clazz, method);
		fireContainerEvent("addPostConstructMethod", clazz);
	}

	@Override
	public void removePostConstructMethod(String clazz) {
		postConstructMethods.remove(clazz);
		fireContainerEvent("removePostConstructMethod", clazz);
	}

	@Override
	public void addPreDestroyMethod(String clazz, String method) {
		if (clazz == null || method == null)
			throw new IllegalArgumentException(
					sm.getString("standardContext.predestroy.required"));
		if (preDestroyMethods.get(clazz) != null)
			throw new IllegalArgumentException(sm.getString(
					"standardContext.predestroy.duplicate", clazz));

		preDestroyMethods.put(clazz, method);
		fireContainerEvent("addPreDestroyMethod", clazz);
	}

	@Override
	public void removePreDestroyMethod(String clazz) {
		preDestroyMethods.remove(clazz);
		fireContainerEvent("removePreDestroyMethod", clazz);
	}

	@Override
	public String findPostConstructMethod(String clazz) {
		return postConstructMethods.get(clazz);
	}

	@Override
	public String findPreDestroyMethod(String clazz) {
		return preDestroyMethods.get(clazz);
	}

	@Override
	public Map<String, String> findPostConstructMethods() {
		return postConstructMethods;
	}

	@Override
	public Map<String, String> findPreDestroyMethods() {
		return preDestroyMethods;
	}

	/**
	 * Set the appropriate context attribute for our work directory.
	 */
	private void postWorkDirectory() {

		// Acquire (or calculate) the work directory path
		String workDir = getWorkDir();
		if (workDir == null || workDir.length() == 0) {

			// Retrieve our parent (normally a host) name
			String hostName = null;
			String engineName = null;
			String hostWorkDir = null;
			Container parentHost = getParent();
			if (parentHost != null) {
				hostName = parentHost.getName();
				if (parentHost instanceof StandardHost) {
					hostWorkDir = ((StandardHost) parentHost).getWorkDir();
				}
				Container parentEngine = parentHost.getParent();
				if (parentEngine != null) {
					engineName = parentEngine.getName();
				}
			}
			if ((hostName == null) || (hostName.length() < 1))
				hostName = "_";
			if ((engineName == null) || (engineName.length() < 1))
				engineName = "_";

			String temp = getName();
			if (temp.startsWith("/"))
				temp = temp.substring(1);
			temp = temp.replace('/', '_');
			temp = temp.replace('\\', '_');
			if (temp.length() < 1)
				temp = "_";
			if (hostWorkDir != null) {
				workDir = hostWorkDir + File.separator + temp;
			} else {
				workDir = "work" + File.separator + engineName +
						File.separator + hostName + File.separator + temp;
			}
			setWorkDir(workDir);
		}

		// Create this directory if necessary
		File dir = new File(workDir);
		if (!dir.isAbsolute()) {
			File catalinaHome = engineBase();
			String catalinaHomePath = null;
			try {
				catalinaHomePath = catalinaHome.getCanonicalPath();
				dir = new File(catalinaHomePath, workDir);
			} catch (IOException e) {
				log.warn(sm.getString("standardContext.workCreateException",
						workDir, catalinaHomePath, getName()), e);
			}
		}
		if (!dir.mkdirs() && !dir.isDirectory()) {
			log.warn(sm.getString("standardContext.workCreateFail", dir,
					getName()));
		}

		// Set the appropriate servlet context attribute
		if (context == null) {
			getServletContext();
		}
		context.setAttribute(ServletContext.TEMPDIR, dir);
		context.setAttributeReadOnly(ServletContext.TEMPDIR);
	}


	// ------------------------------------------------------------- Operations

	/**
	 * Validate the syntax of a proposed <code>&lt;url-pattern&gt;</code>
	 * for conformance with specification requirements.
	 *
	 * @param urlPattern URL pattern to be validated
	 */
	private boolean validateURLPattern(String urlPattern) {

		if (urlPattern == null)
			return (false);
		if (urlPattern.indexOf('\n') >= 0 || urlPattern.indexOf('\r') >= 0) {
			return (false);
		}
		if (urlPattern.equals("")) {
			return true;
		}
		if (urlPattern.startsWith("*.")) {
			if (urlPattern.indexOf('/') < 0) {
				checkUnusualURLPattern(urlPattern);
				return (true);
			} else
				return (false);
		}
		if ((urlPattern.startsWith("/")) &&
				(urlPattern.indexOf("*.") < 0)) {
			checkUnusualURLPattern(urlPattern);
			return (true);
		} else
			return (false);

	}

	/**
	 * Check for unusual but valid <code>&lt;url-pattern&gt;</code>s.
	 * See Bugzilla 34805, 43079 & 43080
	 */
	private void checkUnusualURLPattern(String urlPattern) {
		if (log.isInfoEnabled()) {
			// First group checks for '*' or '/foo*' style patterns
			// Second group checks for *.foo.bar style patterns
			if ((urlPattern.endsWith("*") && (urlPattern.length() < 2 ||
					urlPattern.charAt(urlPattern.length() - 2) != '/')) ||
					urlPattern.startsWith("*.") && urlPattern.length() > 2 &&
							urlPattern.lastIndexOf('.') > 1) {
				log.info("Suspicious url pattern: \"" + urlPattern + "\"" +
						" in context [" + getName() + "] - see" +
						" sections 12.1 and 12.2 of the Servlet specification");
			}
		}
	}

	/**
	 * JSR77 deploymentDescriptor attribute
	 *
	 * @return string deployment descriptor
	 */
	public String getDeploymentDescriptor() {

		InputStream stream = null;
		ServletContext servletContext = getServletContext();
		if (servletContext != null) {
			stream = servletContext.getResourceAsStream(
					org.apache.catalina.startup.Constants.ApplicationWebXml);
		}
		if (stream == null) {
			return "";
		}
		StringBuilder sb = new StringBuilder();
		BufferedReader br = null;
		try {
			br = new BufferedReader(new InputStreamReader(stream));
			String strRead = "";
			while (strRead != null) {
				sb.append(strRead);
				strRead = br.readLine();
			}
		} catch (IOException e) {
			return "";
		} finally {
			if (br != null) {
				try {
					br.close();
				} catch (IOException ioe) {/*Ignore*/}
			}
		}

		return sb.toString();
	}

	/**
	 * JSR77 servlets attribute
	 *
	 * @return list of all servlets ( we know about )
	 */
	public String[] getServlets() {

		String[] result = null;

		Container[] children = findChildren();
		if (children != null) {
			result = new String[children.length];
			for (int i = 0; i < children.length; i++) {
				result[i] = children[i].getObjectName().toString();
			}
		}

		return result;
	}

	@Override
	protected String getObjectNameKeyProperties() {

		StringBuilder keyProperties =
				new StringBuilder("j2eeType=WebModule,");
		keyProperties.append(getObjectKeyPropertiesNameOnly());
		keyProperties.append(",J2EEApplication=");
		keyProperties.append(getJ2EEApplication());
		keyProperties.append(",J2EEServer=");
		keyProperties.append(getJ2EEServer());

		return keyProperties.toString();
	}

	private String getObjectKeyPropertiesNameOnly() {
		StringBuilder result = new StringBuilder("name=//");
		String hostname = getParent().getName();
		if (hostname == null) {
			result.append("DEFAULT");
		} else {
			result.append(hostname);
		}

		String contextName = getName();
		if (!contextName.startsWith("/")) {
			result.append('/');
		}
		result.append(contextName);

		return result.toString();
	}

	@Override
	protected void initInternal() throws LifecycleException {
		super.initInternal();

		if (processTlds) {
			this.addLifecycleListener(new TldConfig());
		}

		// Register the naming resources
		if (namingResources != null) {
			namingResources.init();
		}

		// Send j2ee.object.created notification
		if (this.getObjectName() != null) {
			Notification notification = new Notification("j2ee.object.created",
					this.getObjectName(), sequenceNumber.getAndIncrement());
			broadcaster.sendNotification(notification);
		}
	}

	/* Remove a JMX notificationListener
     * @see javax.management.NotificationEmitter#removeNotificationListener(javax.management.NotificationListener, javax.management.NotificationFilter, java.lang.Object)
     */
	@Override
	public void removeNotificationListener(NotificationListener listener,
	                                       NotificationFilter filter, Object object) throws ListenerNotFoundException {
		broadcaster.removeNotificationListener(listener, filter, object);
	}

	/* Get JMX Broadcaster Info
     * @TODO use StringManager for international support!
     * @TODO This two events we not send j2ee.state.failed and j2ee.attribute.changed!
     * @see javax.management.NotificationBroadcaster#getNotificationInfo()
     */
	@Override
	public MBeanNotificationInfo[] getNotificationInfo() {
		// FIXME: i18n
		if (notificationInfo == null) {
			notificationInfo = new MBeanNotificationInfo[]{
					new MBeanNotificationInfo(new String[]{
							"j2ee.object.created"},
							Notification.class.getName(),
							"web application is created"
					),
					new MBeanNotificationInfo(new String[]{
							"j2ee.state.starting"},
							Notification.class.getName(),
							"change web application is starting"
					),
					new MBeanNotificationInfo(new String[]{
							"j2ee.state.running"},
							Notification.class.getName(),
							"web application is running"
					),
					new MBeanNotificationInfo(new String[]{
							"j2ee.state.stopping"},
							Notification.class.getName(),
							"web application start to stopped"
					),
					new MBeanNotificationInfo(new String[]{
							"j2ee.object.stopped"},
							Notification.class.getName(),
							"web application is stopped"
					),
					new MBeanNotificationInfo(new String[]{
							"j2ee.object.deleted"},
							Notification.class.getName(),
							"web application is deleted"
					)
			};

		}

		return notificationInfo;
	}

	/* Add a JMX-NotificationListener
     * @see javax.management.NotificationBroadcaster#addNotificationListener(javax.management.NotificationListener, javax.management.NotificationFilter, java.lang.Object)
     */
	@Override
	public void addNotificationListener(NotificationListener listener,
	                                    NotificationFilter filter, Object object) throws IllegalArgumentException {
		broadcaster.addNotificationListener(listener, filter, object);
	}


	// ------------------------------------------------------------- Attributes

	/**
	 * Remove a JMX-NotificationListener
	 *
	 * @see javax.management.NotificationBroadcaster#removeNotificationListener(javax.management.NotificationListener)
	 */
	@Override
	public void removeNotificationListener(NotificationListener listener)
			throws ListenerNotFoundException {
		broadcaster.removeNotificationListener(listener);
	}

	/**
	 * Return the naming resources associated with this web application.
	 */
	@Deprecated
	public javax.naming.directory.DirContext getStaticResources() {

		return getResources();

	}

	/**
	 * Return the naming resources associated with this web application.
	 * FIXME: Fooling introspection ...
	 */
	@Deprecated
	public javax.naming.directory.DirContext findStaticResources() {

		return getResources();

	}

	/**
	 * Return the naming resources associated with this web application.
	 */
	public String[] getWelcomeFiles() {

		return findWelcomeFiles();

	}

	@Override
	public boolean getXmlNamespaceAware() {
		return webXmlNamespaceAware;
	}

	@Override
	public void setXmlNamespaceAware(boolean webXmlNamespaceAware) {
		this.webXmlNamespaceAware = webXmlNamespaceAware;
	}

	@Override
	public boolean getXmlValidation() {
		return webXmlValidation;
	}

	@Override
	public void setXmlValidation(boolean webXmlValidation) {
		this.webXmlValidation = webXmlValidation;
	}

	@Override
	public boolean getTldNamespaceAware() {
		return true;
	}

	@Override
	public void setTldNamespaceAware(boolean tldNamespaceAware) {
		// NO-OP;
	}

	@Override
	public boolean getXmlBlockExternal() {
		return xmlBlockExternal;
	}

	@Override
	public void setXmlBlockExternal(boolean xmlBlockExternal) {
		this.xmlBlockExternal = xmlBlockExternal;
	}

	@Override
	public boolean getTldValidation() {
		return tldValidation;
	}

	@Override
	public void setTldValidation(boolean tldValidation) {
		this.tldValidation = tldValidation;
	}

	/**
	 * Returns the processTlds attribute value.
	 */
	public boolean getProcessTlds() {
		return processTlds;
	}

	/**
	 * Sets the process TLDs attribute.
	 *
	 * @param newProcessTlds The new value
	 */
	public void setProcessTlds(boolean newProcessTlds) {
		processTlds = newProcessTlds;
	}

	/**
	 * Support for "stateManageable" JSR77
	 */
	public boolean isStateManageable() {
		return true;
	}

	@Deprecated
	public void startRecursive() throws LifecycleException {
		// nothing to start recursive, the servlets will be started by load-on-startup
		start();
	}

	public String getServer() {
		return server;
	}

	public String setServer(String server) {
		return this.server = server;
	}

	public String[] getJavaVMs() {
		return javaVMs;
	}

	public String[] setJavaVMs(String[] javaVMs) {
		return this.javaVMs = javaVMs;
	}

	/**
	 * Gets the time this context was started.
	 *
	 * @return Time (in milliseconds since January 1, 1970, 00:00:00) when this
	 * context was started
	 */
	public long getStartTime() {
		return startTime;
	}

	@Deprecated
	public boolean isEventProvider() {
		return false;
	}

	@Deprecated
	public boolean isStatisticsProvider() {
		return false;
	}

	/**
	 * A helper class to manage the filter mappings in a Context.
	 */
	private static final class ContextFilterMaps {
		private final Object lock = new Object();

		/**
		 * The set of filter mappings for this application, in the order they
		 * were defined in the deployment descriptor with additional mappings
		 * added via the {@link ServletContext} possibly both before and after
		 * those defined in the deployment descriptor.
		 */
		private FilterMap[] array = new FilterMap[0];

		/**
		 * Filter mappings added via {@link ServletContext} may have to be
		 * inserted before the mappings in the deployment descriptor but must be
		 * inserted in the order the {@link ServletContext} methods are called.
		 * This isn't an issue for the mappings added after the deployment
		 * descriptor - they are just added to the end - but correctly the
		 * adding mappings before the deployment descriptor mappings requires
		 * knowing where the last 'before' mapping was added.
		 */
		private int insertPoint = 0;

		/**
		 * Return the set of filter mappings.
		 */
		public FilterMap[] asArray() {
			synchronized (lock) {
				return array;
			}
		}

		/**
		 * Add a filter mapping at the end of the current set of filter
		 * mappings.
		 *
		 * @param filterMap The filter mapping to be added
		 */
		public void add(FilterMap filterMap) {
			synchronized (lock) {
				FilterMap results[] = Arrays.copyOf(array, array.length + 1);
				results[array.length] = filterMap;
				array = results;
			}
		}

		/**
		 * Add a filter mapping before the mappings defined in the deployment
		 * descriptor but after any other mappings added via this method.
		 *
		 * @param filterMap The filter mapping to be added
		 */
		public void addBefore(FilterMap filterMap) {
			synchronized (lock) {
				FilterMap results[] = new FilterMap[array.length + 1];
				System.arraycopy(array, 0, results, 0, insertPoint);
				System.arraycopy(array, insertPoint, results, insertPoint + 1,
						array.length - insertPoint);
				results[insertPoint] = filterMap;
				array = results;
				insertPoint++;
			}
		}

		/**
		 * Remove a filter mapping.
		 *
		 * @param filterMap The filter mapping to be removed
		 */
		public void remove(FilterMap filterMap) {
			synchronized (lock) {
				// Make sure this filter mapping is currently present
				int n = -1;
				for (int i = 0; i < array.length; i++) {
					if (array[i] == filterMap) {
						n = i;
						break;
					}
				}
				if (n < 0)
					return;

				// Remove the specified filter mapping
				FilterMap results[] = new FilterMap[array.length - 1];
				System.arraycopy(array, 0, results, 0, n);
				System.arraycopy(array, n + 1, results, n, (array.length - 1)
						- n);
				array = results;
				if (n < insertPoint) {
					insertPoint--;
				}
			}
		}
	}

	private static class NoPluggabilityServletContext
			implements ServletContext {

		private final ServletContext sc;

		public NoPluggabilityServletContext(ServletContext sc) {
			this.sc = sc;
		}

		@Override
		public String getContextPath() {
			return sc.getContextPath();
		}

		@Override
		public ServletContext getContext(String uripath) {
			return sc.getContext(uripath);
		}

		@Override
		public int getMajorVersion() {
			return sc.getMajorVersion();
		}

		@Override
		public int getMinorVersion() {
			return sc.getMinorVersion();
		}

		@Override
		public int getEffectiveMajorVersion() {
			throw new UnsupportedOperationException(
					sm.getString("noPluggabilityServletContext.notAllowed"));
		}

		@Override
		public int getEffectiveMinorVersion() {
			throw new UnsupportedOperationException(
					sm.getString("noPluggabilityServletContext.notAllowed"));
		}

		@Override
		public String getMimeType(String file) {
			return sc.getMimeType(file);
		}

		@Override
		public Set<String> getResourcePaths(String path) {
			return sc.getResourcePaths(path);
		}

		@Override
		public URL getResource(String path) throws MalformedURLException {
			return sc.getResource(path);
		}

		@Override
		public InputStream getResourceAsStream(String path) {
			return sc.getResourceAsStream(path);
		}

		@Override
		public RequestDispatcher getRequestDispatcher(String path) {
			return sc.getRequestDispatcher(path);
		}

		@Override
		public RequestDispatcher getNamedDispatcher(String name) {
			return sc.getNamedDispatcher(name);
		}

		@Override
		@Deprecated
		public Servlet getServlet(String name) throws ServletException {
			return sc.getServlet(name);
		}

		@Override
		@Deprecated
		public Enumeration<Servlet> getServlets() {
			return sc.getServlets();
		}

		@Override
		@Deprecated
		public Enumeration<String> getServletNames() {
			return sc.getServletNames();
		}

		@Override
		public void log(String msg) {
			sc.log(msg);
		}

		@Override
		@Deprecated
		public void log(Exception exception, String msg) {
			sc.log(exception, msg);
		}

		@Override
		public void log(String message, Throwable throwable) {
			sc.log(message, throwable);
		}

		@Override
		public String getRealPath(String path) {
			return sc.getRealPath(path);
		}

		@Override
		public String getServerInfo() {
			return sc.getServerInfo();
		}

		@Override
		public String getInitParameter(String name) {
			return sc.getInitParameter(name);
		}

		@Override
		public Enumeration<String> getInitParameterNames() {
			return sc.getInitParameterNames();
		}

		@Override
		public boolean setInitParameter(String name, String value) {
			throw new UnsupportedOperationException(
					sm.getString("noPluggabilityServletContext.notAllowed"));
		}

		@Override
		public Object getAttribute(String name) {
			return sc.getAttribute(name);
		}

		@Override
		public Enumeration<String> getAttributeNames() {
			return sc.getAttributeNames();
		}

		@Override
		public void setAttribute(String name, Object object) {
			sc.setAttribute(name, object);
		}

		@Override
		public void removeAttribute(String name) {
			sc.removeAttribute(name);
		}

		@Override
		public String getServletContextName() {
			return sc.getServletContextName();
		}

		@Override
		public Dynamic addServlet(String servletName, String className) {
			throw new UnsupportedOperationException(
					sm.getString("noPluggabilityServletContext.notAllowed"));
		}

		@Override
		public Dynamic addServlet(String servletName, Servlet servlet) {
			throw new UnsupportedOperationException(
					sm.getString("noPluggabilityServletContext.notAllowed"));
		}

		@Override
		public Dynamic addServlet(String servletName,
		                          Class<? extends Servlet> servletClass) {
			throw new UnsupportedOperationException(
					sm.getString("noPluggabilityServletContext.notAllowed"));
		}

		@Override
		public <T extends Servlet> T createServlet(Class<T> c)
				throws ServletException {
			throw new UnsupportedOperationException(
					sm.getString("noPluggabilityServletContext.notAllowed"));
		}

		@Override
		public ServletRegistration getServletRegistration(String servletName) {
			throw new UnsupportedOperationException(
					sm.getString("noPluggabilityServletContext.notAllowed"));
		}

		@Override
		public Map<String, ? extends ServletRegistration> getServletRegistrations() {
			throw new UnsupportedOperationException(
					sm.getString("noPluggabilityServletContext.notAllowed"));
		}

		@Override
		public javax.servlet.FilterRegistration.Dynamic addFilter(
				String filterName, String className) {
			throw new UnsupportedOperationException(
					sm.getString("noPluggabilityServletContext.notAllowed"));
		}

		@Override
		public javax.servlet.FilterRegistration.Dynamic addFilter(
				String filterName, Filter filter) {
			throw new UnsupportedOperationException(
					sm.getString("noPluggabilityServletContext.notAllowed"));
		}

		@Override
		public javax.servlet.FilterRegistration.Dynamic addFilter(
				String filterName, Class<? extends Filter> filterClass) {
			throw new UnsupportedOperationException(
					sm.getString("noPluggabilityServletContext.notAllowed"));
		}

		@Override
		public <T extends Filter> T createFilter(Class<T> c)
				throws ServletException {
			throw new UnsupportedOperationException(
					sm.getString("noPluggabilityServletContext.notAllowed"));
		}

		@Override
		public FilterRegistration getFilterRegistration(String filterName) {
			throw new UnsupportedOperationException(
					sm.getString("noPluggabilityServletContext.notAllowed"));
		}

		@Override
		public Map<String, ? extends FilterRegistration> getFilterRegistrations() {
			throw new UnsupportedOperationException(
					sm.getString("noPluggabilityServletContext.notAllowed"));
		}

		@Override
		public SessionCookieConfig getSessionCookieConfig() {
			throw new UnsupportedOperationException(
					sm.getString("noPluggabilityServletContext.notAllowed"));
		}

		@Override
		public void setSessionTrackingModes(
				Set<SessionTrackingMode> sessionTrackingModes) {
			throw new UnsupportedOperationException(
					sm.getString("noPluggabilityServletContext.notAllowed"));
		}

		@Override
		public Set<SessionTrackingMode> getDefaultSessionTrackingModes() {
			throw new UnsupportedOperationException(
					sm.getString("noPluggabilityServletContext.notAllowed"));
		}

		@Override
		public Set<SessionTrackingMode> getEffectiveSessionTrackingModes() {
			throw new UnsupportedOperationException(
					sm.getString("noPluggabilityServletContext.notAllowed"));
		}

		@Override
		public void addListener(String className) {
			throw new UnsupportedOperationException(
					sm.getString("noPluggabilityServletContext.notAllowed"));
		}

		@Override
		public <T extends EventListener> void addListener(T t) {
			throw new UnsupportedOperationException(
					sm.getString("noPluggabilityServletContext.notAllowed"));
		}

		@Override
		public void addListener(Class<? extends EventListener> listenerClass) {
			throw new UnsupportedOperationException(
					sm.getString("noPluggabilityServletContext.notAllowed"));
		}

		@Override
		public <T extends EventListener> T createListener(Class<T> c)
				throws ServletException {
			throw new UnsupportedOperationException(
					sm.getString("noPluggabilityServletContext.notAllowed"));
		}

		@Override
		public JspConfigDescriptor getJspConfigDescriptor() {
			throw new UnsupportedOperationException(
					sm.getString("noPluggabilityServletContext.notAllowed"));
		}

		@Override
		public ClassLoader getClassLoader() {
			throw new UnsupportedOperationException(
					sm.getString("noPluggabilityServletContext.notAllowed"));
		}

		@Override
		public void declareRoles(String... roleNames) {
			throw new UnsupportedOperationException(
					sm.getString("noPluggabilityServletContext.notAllowed"));
		}
	}
}
